Compiling Class Member Variables Out with C++ Temp

2019-07-17 11:08发布

问题:

I have a class that looks similar to this:

class Compound
{
    void* pValue0;
    void* pValue1;
    void* pValue2;
    void* pValue3;
    void* pValue4;
    void* pValue5;
    void* pValue6;
    void* pValue7;
    void* pValue8;
    void* pValue9;
    void* pValueA;
    void* pValueB;
    void* pValueC;
};

When I create a new Compound class, I allocate extra memory [sizeof(Compound) + extraSpace]. Each of the pValue's refer to an address in the extra memory.

Now, I would like to reduce the number of pValue's depending on which of them I need. Templates seem like a good fit.

So if I wanted a class Compound<0A>, I would only want pValue0 and pValueA, and then have the compiler remove all other pValues. Essentially, I would want it to then become:

template <uint Mask = 0A>
class Compound<Mask>
{
    void* pValue0;
    void* pValueA;
}

Is this possible? I got close with enable_if, but when I tried to limit it to a particular mask the compiler threw errors about being unable to find a type when the enable_if case was false.

Thank you all!

回答1:

This might do:

template<char...>
struct flags_tag {constexpr flags_tag(){}; };

template<char...Cs>
struct make_flags{ using type=flags_tag<Cs...>; };
template<char...Cs>
struct make_flags<'0','x',Cs...>:make_flags<Cs...>{};
template<char...Cs>
struct make_flags<'0','X',Cs...>:make_flags<Cs...>{};
template<char...Cs>
using make_flags_t = typename make_flags<Cs...>::type;

template<char...Cs>
constexpr make_flags_t<Cs...> operator""_flag(){ return {}; }

template<char> struct pValue_t;
template<> struct pValue_t<'0'>{ void* pValue0 = 0; };
template<> struct pValue_t<'1'>{ void* pValue1 = 0; };
// ...
template<> struct pValue_t<'A'>{ void* pValueA = 0; };
template<> struct pValue_t<'B'>{ void* pValueB = 0; };
template<> struct pValue_t<'C'>{ void* pValueC = 0; };

template<class flags>
struct Compound;

template<char...Cs>
struct Compound< flags_tag<Cs...> >:
  pValue_t<Cs>...
{};

Then you use it like:

using my_type = Compound< decltype( 0x0A_flag ) >;
int main() {
  my_type test;
  std::cout << test.pValue0 << test.pValueA << '\n';
}

which seems to do what you want.

I'd also disable the copy/move ctor of your Compound type, and make its other constructors private with a friend factory function.

Note that this code can generate an exponential number of classes (2^12, or 4k), and that can cause binary bloat (if any per-class code isn't inlined out of existence).

[live example]



回答2:

std::conditional is similar to std::enable_if, except conditional always returns a type.

std::conditional<satisfies(Mask), void*, EmptyClass> would be the way to swap the member's types out conditionally and have it compile.

The problem is, C++ does not allow empty members. The size will be 1. To solve this problem, you'd need the empty member optimization. This will achieve the memory layout that you're asking for, but unfortunately, this will make the class members difficult to read. You can add accessor functions to mitigate this, if you think it's worth pursuing.



回答3:

I am assuming that the Compound class shall contain pointers to objects of different classes and not void pointers, as you said in your comment.

I don't know if that is a good idea because I think there is something wrong in the design, but you could use tuples and template specializations on ints to select the type you want:

class Class0;
class ClassA;

template<int> struct Compound {};
template<> struct Compound<0A> 
{
    typedef std::tuple<Class0*, ClassA*> type;
}
// other mapping from mask to type

typename Compound<0A>::type aCompund(ptr0, ptrA);