Search code examples
c++classstructforward-declaration

mutually dependent local classes (or mutually recursive lambdas)


I often create local helper classes inside methods, wherever such a class is locally useful but irrelevant outside the method. I just came across a case where I would like two local classes that are mutually dependent.

The idea is to have the following pattern:

void SomeClass::someMethod()
{
    struct A
    {
        B * b;
        void foo() { if(b) b->bar(); };
    };
    struct B
    {
        A * a;
        void bar() { if(a) a->foo(); }
    };
}

But it doesn't compile because A needs B. Forward declaring B helps so that the line B * b; compiles, but still the method A::foo() needs the full declaration of B and the compiler complains.

I see two workarounds:

  1. Declaring and defining the classes in SomeClass.cpp, before SomeClass::someMethod(). I feel it's not elegant since not only the classes are not local to SomeClass::someMethod(), but not even local to SomeClass.

  2. Declaring the classes in SomeClass.h nested in SomeClass, and defining them in SomeClass.cpp. I do not like this solution either because because not only the classes are not local to SomeClass::someMethod(), but it does pollute SomeClass.h for no good reason other than a limitation of the language.

Hence two questions: is it possible at all to have the classes local to SomeClass::someMethod()? If not, do you see more elegant workarounds?


Solution

  • Since the answer seems to be: "it is not possible to have clean mutually dependent local classes", it turns out that the workaround I like the most is to move the logic outside the structs themselves. As suggested by remyabel in the comments of the question, it can be done by creating a third class, but my favorite approach is to create mutually recursive lambda functions, since it makes possible to capture variables (hence makes my life easier in my real case usage). So it looks like:

    #include <functional>
    #include <iostream>
    
    int main()
    {
        struct B;
        struct A { B * b; };
        struct B { A * a; };
    
        std::function< void(A *) > foo;
        std::function< void(B *) > bar;
    
        foo = [&] (A * a) 
        {
            std::cout << "calling foo" << std::endl;
            if(a->b) { bar(a->b); }
        };
        bar = [&] (B * b)
        {
            std::cout << "calling bar" << std::endl;
            if(b->a) { foo(b->a); }
        };
    
        A a = {0};
        B b = {&a};
        foo(&a);
        bar(&b);
    
        return 0;
    }
    

    That compiles and prints:

    calling foo
    calling bar
    calling foo
    

    Note that the type of the lambdas must be manually specified because type inference doesn't work well with recursive lambdas.