4
votes

In the code below (C++14, no 'fold' from C++17), I am trying to automatically compute fixed offsets for the field of a class, at compile time, using boost fusion fold, parameter packs and a lambda. Unfortunately, this results in a compile time error... Is it possible to do something like this?

[EDIT: something else bothers me too: this is not exactly what I want. I would like the _size of ControlledLayout2 to be available at compile time (that's why I made it static), not just when the constructor will be called]

template <typename T, uint32_t size>
struct Field2
{
    typedef T _type;
    static const uint32_t _size;
    static uint32_t _offset;
};

template <typename T, uint32_t size>
const uint32_t Field2<T,size>::_size = size;

template <typename T, uint32_t size>
uint32_t Field2<T,size>::_offset = 0;

template <typename ... T>
struct ControlledLayout2
{
    static uint32_t _size;

    ControlledLayout2(T... args) {
      _size = fold({args...}, 0, 
                   [&](uint32_t s, T field) { return T::_offset = s + T::_size; }...);
    };
};

...
ControlledLayout2<Field2<int, 32>, Field2<char, 1>, Field2<long, 64>> cl;
cout << cl._size << endl;
...

And the compiler error is:

error: parameter not expanded with '...';
_size = accumulate({args...}, ...
1
The error looks like GCC. What version?Drew Dormann
GCC 4.8.3.4 - but of course, it's my code that's guilty, not GCC, right?Frank
T field -> T is not expandedPiotr Skotnicki
Piotr - ok - but I'm confused then - if I expand with '...' between T and field, the compiler doesn't complain at this place anymore, but I want one type at a time inside the lambda body - is that what it's going to do?Frank
then you should put ... after } closing the lambda expression (though it fails in GCC)Piotr Skotnicki

1 Answers

2
votes

Since you want to do all the calculation at compile time, boost::fusion::fold is not the right tool for this.

Instead I would compute size and offset using constexpr in ControlledLayout2:

#include <iostream>
#include <tuple>

template <typename T, uint32_t Size>
struct Field2
{
    using type = T;
    static const uint32_t size = Size;
};

template <typename T, typename U>
struct selector;

template <typename T, std::size_t... Is>
struct selector<T, std::index_sequence<Is...>>
{
    using type = std::tuple<typename std::tuple_element<Is, T>::type...>;
};

template <std::size_t N, typename... Ts>
struct remove_last_n
{
    using Indices = std::make_index_sequence<sizeof...(Ts)-N>;  
    using type = typename selector<std::tuple<Ts...>, Indices>::type;
};

template <typename ... Ts>
struct ControlledLayout2 
{    
    static constexpr uint32_t get_size()
    {
        return size_impl<Ts...>();
    }

    template <typename X, typename... Xs>
    static constexpr uint32_t size_impl(typename std::enable_if<(sizeof...(Xs) > 0)>::type* = 0)
    {
        return ((X::size) + size_impl<Xs...>());
    }

    template <typename X>
    static constexpr uint32_t size_impl()
    {
        return X::size;
    }

    template <std::size_t field_number>
    static constexpr uint32_t offset()
    {
        using Tuple = typename remove_last_n<sizeof...(Ts)-field_number, Ts...>::type;
        return offset_impl(Tuple{});
    }

    template <typename... Xs>
    static constexpr uint32_t offset_impl(std::tuple<Xs...>)
    {
        return size_impl<Xs...>();
    }

    static const uint32_t size = get_size();
};

int main()
{
    using Layout  = ControlledLayout2<Field2<int, 32>,
                                      Field2<char, 1>,
                                      Field2<char, 128>,
                                      Field2<long, 64>
                                      >;
    std::cout << Layout::size << std::endl;
    std::cout << Layout::offset<3>() << std::endl;
}

output

225
161

live on coliru