Home > Software design >  Is there a technique for named instances of an anonymous struct to reference functions inside the en
Is there a technique for named instances of an anonymous struct to reference functions inside the en

Time:02-15

I have a CRTP class where for API clarity during refactoring, I want to have a named anonymous struct containing methods, instead of having all methods at class scope. The problem is, these methods need access to the outer scope. For example:

template<typename T>
class sample_class {
public:
   struct  {
      void do_something() {
          auto& result = get_ref().something_else(); //get_ref() out of inner struct scope
          ...
      }
   } inner;

private:
  T& get_ref() { return static_cast<T&>(*this); }
};

Is there some technique to make this work? Specifically C 14 and gcc7, since I do not believe anonymous structs are technically standard compliant.

CodePudding user response:

A class in another class has no implicit pointer to the enclosing class's this pointer.

If you want it to have the pointer to an instance of the enclosing class, explicitly store it.

struct  {
  void do_something() {
    auto& result = p_sample->get_ref().something_else(); //get_ref() out of inner struct scope
    ...
  }
  sample* p_sample;
} inner;

or pass in a pointer to the methods:

  void do_something(sample* psample) {
    auto& result = p_sample->get_ref().something_else(); //get_ref() out of inner struct scope
    ...
  }

there are ways to use pointer arithmetic to generate what appears to be a pointer to the outer class, but they are trapped with extremely complex and dangerous rules in C .

Other than some access/naming rules and the like, classes defined in other classes are not magic. They could (in theory) exist in other environments; the inner could live on the stack somewhere, not within a sample.

CodePudding user response:

Yes, you subtract the result of offsetof from this:

auto &self = *reinterpret_cast<sample_class *>(reinterpret_cast<char *>(this) - offsetof(sample_class, inner));

This might technically be UB (see reachability rules for std::launder), but should be good enough in practice.

But I argue that getting a nice-looking method name doesn't warrant this hackery. Just replace inner.do_something() with something like inner_do_something().

  • Related