Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing the offset of a field as a template parameter to that field

What I am trying to do is to have a class which is aware of its offset within an enclosing class with no runtime overhead at all. Here's an example of what I wish I could do:

template<int offset>
struct Inner {
};

struct Outer {
   int placeholder;
   Inner<offsetof(Outer, ptr)> ptr; 
};

The above code doesn't compile because offsetof(Outer, ptr) doesn't know about ptr (it's helping define it). I have implemented a few versions of this same idea that do incur runtime overheads (both in memory and executed instructions), but I'm having trouble implementing a "0 runtime overhead" version like my dream implementation above. Any ideas how this can be done?

like image 834
antonin_scalia Avatar asked Oct 29 '25 02:10

antonin_scalia


1 Answers

  1. Only the outer type can know the offset to the inner member sub-object
  2. Therefore the member sub-object type needs to know about the outer type
  3. Luckily, this sort of thing comes up fairly frequently. Indeed, the frequency with which it recurs could be considered ... curious.
  4. Unfortunately, no-one has come up with a good name for it. So, it's called the Curiously Recurring Template Pattern or CRTP for short.

The hard thing is figuring out a way for the outer type to automate production of the inner offsets, without having to write each one by hand. Doing it by hand is easy, but tedious, eg.

// use an enum to create distinct types
template<typename Outer, typename Outer::FieldId ID>
struct Inner
{
    static constexpr size_t offset();
};

struct Outer
{
    enum FieldId { First, Second };

    int header;
    Inner<Outer, FieldId::First> first;
    double interstitial;
    Inner<Outer, FieldId::Second> second;

    static constexpr size_t offset_of(std::integral_constant<FieldId, FieldId::First>) { return offsetof(Outer, first); }
    static constexpr size_t offset_of(std::integral_constant<FieldId, FieldId::Second>) { return offsetof(Outer, second); }
};

template<typename Outer, typename Outer::FieldId ID>
constexpr size_t Inner<Outer, ID>::offset()
{
    return Outer::offset_of(std::integral_constant<decltype(ID), ID> {});
}

This is clunky, partly because of the std::integral_constant wrapper (which could be avoided or typedef'd), but mostly because the ID-to-field mapping has to be expressed manually in code.

Automating production is hard without compile-time reflection. You can automate everything if you just use a tuple-like object instead of a struct at the top level, but that makes it harder to interleave "smart" and dumb members, and probably changes the layout, and it definitely breaks the StandardLayoutType requirements, which may prevent offsetof from working entirely.

like image 127
Useless Avatar answered Oct 30 '25 17:10

Useless



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!