2

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.

Chuu
  • 4,301
  • 2
  • 28
  • 54
  • 1
    What is a "named anonymous struct"? – Vlad from Moscow Feb 14 '22 at 20:33
  • 1
    @VladfromMoscow The name I am giving to the type of struct "inner" is. Chat suggested the name "named instance of an anonymous struct" which is technically more correct. I do not know if there is a better term for these types of structs. – Chuu Feb 14 '22 at 20:35

2 Answers2

3

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • I was thinking along these lines, but I was hoping there was some solution that didn't require explicitly storing a "this" pointer without touching the way the method is called. – Chuu Feb 14 '22 at 20:27
1

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().

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207