在多继承中获取基类的编译时常量offsetof

时间:2017-09-20 16:34:20

标签: c++

看看这个例子:

struct s77 {
    char d[77];
};

struct s1 {
    char d;
};

struct Foo: s77, s1 {
};

struct Off {
    static const int v = std::size_t(static_cast<s1*>(static_cast<Foo*>(nullptr)+1)) - std::size_t(static_cast<Foo*>(nullptr)+1);
};

此代码尝试将s1Foo的偏移量放入Off::v。此代码使用GCC / clang进行编译(没有任何警告),但无法使用VS2015 / VS2017进行编译(错误C2131:表达式未评估为常量)

哪种编译器正确?

我能否以符合标准的方式实现此功能?如果不可能,是否可以创建一个适用于VS2015 / VS2017的工作解决方案?我愿意接受任何有效的解决方案,即使根据标准有未定义的行为(但恰好与VS2015和VS2017一起工作)。 Off::v必须是编译时常量。

我原来的问题是:我有一个tuple的自己的实现,它是用多重继承实现的(比如clang的tuple)。我想为元组创建一个编译时常量“描述符”,它包含元组中所有成员的偏移量。该描述符也包含每个元组成员的函数指针。如果我手动创建这个描述符,它看起来像这样(例如):

struct Entry {
    int offset;
    void (*function)(void *member);
};

Entry descriptor[] = {
    { 0, &SomeType1::static_function },
    { 12, &SomeType2::static_function },
    { 20, &SomeType3::static_function }
};

这样做的目的是我可以有一个通用函数(不是模板),它可以使用这个描述符在每个元组成员上调用特定于类型的函数:

void call(void *tuple, const Entry *entries, int n) {
    for (int i=0; i<n; i++) {
        entries[i].function(static_cast<char *>(tuple)+entries[i].offset);
    }
}

(此解决方案而不是模板call函数的原因是call实际上是我的实际代码中的一个巨大功能,并且entry[i].function调用无法从中计算出来。我想避免大量的代码重复。)

1 个答案:

答案 0 :(得分:0)

如下:

struct Entry {
    void* (*data_member_getter)(void*);
    void (*function)(void *member);
};

namespace details
{
    template <std::size_t I, typename Tuple>
    constexpr void* voidPGetter(void* tuple)
    {
        return &std::get<I>(*reinterpret_cast<Tuple*>(tuple));
    }

    template <typename Tuple, std::size_t I>
    constexpr MakeEntry()
    {
        using type = std::tuple_element_t<I, Tuple>;
        return { &voidPGetter<I, Tuple>, &type::static_function };
    }

    template <typename Tuple, std::size_t ... Is>
    constexpr std::array<Entry, sizeof...(Is)>
    ComputeEntryHelper(std::index_sequence<Is...>)
    {
        return {{MakeEntry<Is, Tuple>()...}};
    }
}

template <typename Tuple>
constexpt auto ComputeEntry()
{
    constexpr auto size = std::tuple_size<Tuple>::value;
    return details::ComputeEntryHelper(std::make_index_sequence<size>());
}

然后

void call(void* tuple, const Entry* entries, int n) {
    for (int i = 0; i != n; ++i) {
        entries[i].function(entries[i].data_member_getter(tuple));
    }
}

因此,而不是偏移,具有获取数据的功能。