简体   繁体   中英

What is the C++14 alternative to C99 compound literals, without using dynamic allocation?

I'm developing an Embedded System with an ARM processor with an MPU, using C++14, and I want to keep the number of dynamic allocations down, also during initialization, as I prefer to know the exact amount of memory used at link time.

In C99, you can allocate memory on the stack or global scope using compound literals, see this nonsense code, that allocates a struct within a struct at compile-time:

struct Foo {
    struct Foo* next;
};
struct Foo *bar = {
    &(struct Foo){
        &(struct Foo){ NULL }
    }
};

It's not possible to use any GCC or Clang extensions to solve this problem, only pure C++14 code.

The only simple method, that I've found, to mimic this behavior, is to use lambda functions. However, this solution is not reentrant.

struct Foo* bar = [](){
    static struct Foo bar = {
        static struct Foo bar = { nullptr };
        return &bar;
    }();
    return &bar;
}();

The lambda solution would work in my case, and I could use macros to make it more comfortable to use, but it's not a solution if I want to put the data structure on the stack.

What is the best C++ approach to solving this problem (without using dynamic allocation)?

The current problem I'm trying to solve is a tree of derived classes, and I would like to assign it at compile-time.

What I'm trying to achieve in imagined C++ code:

RootClass* b = &(DerivedClass1){ &(DerivedClass2){10},
                                 &(DerivedClass2){20},
                                 &(DerivedClass1){ &(DerivedClass0){} }
                               };

What I'm trying to achieve in imagined C++ code:

Well that's never going to work. There's no requirement that any of those derived classes are the same size as the base class, so it would be impossible to do pointer arithmetic on the base class (even if C++ allowed pointer arithmetic on base class pointers to arrays where the element type is a derived class. Which it does not ).

If you were dealing with a single type T , then you could just do this:

T ts[] = {<initializers>};

And you could use ts as you see fit. But this only works for proper arrays.

Now, if you want to do your derived class thing, you can always use a tuple as the container of the derived class elements, and then build an array of pointers to the base class elements so that you could iterate from one to the other. C++14 makes this a bit cumbersome, as it requires that you list the type in the tuple type itself, but C++17's class template argument deduction makes it more digestible:

auto tpl = tuple(DerivedClass2{10}, DerivedClass2{20}, DerivedClass1{DerivedClass0{}});

The C++14 version just repeats the types.

Building the array requires some template metaprogramming techniques:

template<typename BC, typename Tpl, std::size_t ...ixs>
auto GetBaseClassArrayHelper(Tpl &tpl, std::index_sequence<ixs...>)
{
    return std::array<BC*, std::tuple_size<Tpl>::value>{static_cast<BC*>(std::get<ixs>(tpl))...};
}

template<typename BC, typename Tpl>
std::array<BC*, std::tuple_size<Tpl>::value> GetBaseClassArray(Tpl &tpl)
{
    return GetBaseClassArrayHelper<BC>(tpl, std::make_index_sequence<std::tuple_size<Tpl>::value>{});
}

Now, this doesn't let you take a BaseClass* and ++ it into a different BaseClass* . You'd need to have a pointer into the array of pointers, a BaseClass** in order to do that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM