std::array编译时推导

3

我有一段代码,试图自动解码给定的缓冲区中的数据类型。这些数据以元组的形式表示:

std::tuple<uint8_t, int32_t> data;
size_t bufferIndex;
IOBuffer::ConstSPtr buffer( std::make_shared<IOBuffer>(5) );

我还有元组辅助函数,可以迭代元组并对每个元组执行一个函数对象:
//-------------------------------------------------------------------------
//
template <typename Function, typename ...Tuples, typename ...Args>
void IterateOverTuple( Function& f, std::tuple<Tuples...>& t,
                       Args&... args )
{
    impl::IterateOverTupleImpl<0, sizeof...(Tuples),
        std::tuple<Tuples...>>()( f, t, args... );
}
//---------------------------------------------------------------------
//
template <int I, size_t TSize, typename Tuple>
struct IterateOverTupleImpl
    : public IterateOverTupleImpl<I + 1, TSize, Tuple>
{
    template <typename Function, typename ...Args>
    void operator()( Function& f, Tuple& t, Args&... args )
    {
        f( std::get<I>(t), args... );
        IterateOverTupleImpl<I + 1, TSize, Tuple>::operator()( f, t,
            args... );
    }
};

//---------------------------------------------------------------------
//
template <int I, typename Tuple>
struct IterateOverTupleImpl<I, I, Tuple>
{
    template <typename Function, typename ...Args>
    void operator()( Function& f, Tuple& t, Args&... )
    {
        cl::Ignore(f);
        cl::Ignore(t);
    }
};

这是我的解码器函数对象:

struct DecoderFunctor
{
    template <typename X>
    void DecodeIntegral( X& x, const IOBuffer::ConstSPtr& buffer, size_t& index )
    {
        if( std::is_same<X, uint8_t>::value )
        {
            x = buffer->At(index);
        }
        else if( std::is_same<X,  int8_t>::value )
        {
            x = static_cast<int8_t>( buffer->At(index) );
        }
        else if( std::is_same<X, uint16_t>::value )
        {
            x = cl::ByteConversion::UbytesToUInt16( UByteArray<2>{{
                buffer->At(index + 0),
                buffer->At(index + 1) }}
            );
        }
        else if( std::is_same<X, int16_t>::value )
        {
            x = cl::ByteConversion::UbytesToInt16( UByteArray<2>{{
                buffer->At(index + 0),
                buffer->At(index + 1) }}
            );
        }
        else if( std::is_same<X, uint32_t>::value )
        {
            x = cl::ByteConversion::UbytesToUInt32( UByteArray<4>{{
                buffer->At(index + 0),
                buffer->At(index + 1),
                buffer->At(index + 2),
                buffer->At(index + 3) }}
            );
        }
        else if( std::is_same<X, int32_t>::value )
        {
            x = cl::ByteConversion::UbytesToInt32( UByteArray<4>{{
                buffer->At(index + 0),
                buffer->At(index + 1),
                buffer->At(index + 2),
                buffer->At(index + 3) }}
            );
        }
        else if( std::is_same<X, uint64_t>::value )
        {
            x = cl::ByteConversion::UbytesToUInt64( UByteArray<8>{{
                buffer->At(index + 0),
                buffer->At(index + 1),
                buffer->At(index + 2),
                buffer->At(index + 3),
                buffer->At(index + 4),
                buffer->At(index + 5),
                buffer->At(index + 6),
                buffer->At(index + 7) }}
            );
        }
        else if( std::is_same<X, int64_t>::value )
        {
            x = cl::ByteConversion::UbytesToInt64( UByteArray<8>{{
                buffer->At(index + 0),
                buffer->At(index + 1),
                buffer->At(index + 2),
                buffer->At(index + 3),
                buffer->At(index + 4),
                buffer->At(index + 5),
                buffer->At(index + 6),
                buffer->At(index + 7) }}
            );
        }

        // Increment the index in the buffer
        index += sizeof(X);
    }

    template <typename X>
    void operator()( X& x, const IOBuffer::ConstSPtr& buffer, size_t& index )
    {
        if( std::is_integral<X>::value )
        {
            DecodeIntegral( x, buffer, index );
        }
    }
};

这就是代码被调用的地方:

DecoderFunctor func;
IterateOverTuple( func, data, buffer, index );

对于整数类型,一切都很正常,它们被完美地解码。但是当我想尝试实现一个新的解码方法(用于数组)时,它无法编译:

std::tuple<std::array<uint16_t, 100>, std::array<uint8_t, 100>> data;
这里是错误信息(gcc-4.9)。
因为测试std::is_integral<X>::value,所以数据不应该在DecodeIntegral( x, buffer, index );中被评估。我不明白为什么会出现这个错误。
请注意,这还在进行中,肯定有一些错误和改进需要做。感谢您的帮助!
2个回答

5

我承认我没有浏览所有的代码,但我相信你的问题在于运行时与编译时的条件。你不能使用一个运行时的条件(例如if (std::is_integral<:::>::value>))来防止编译时错误。

我理解当X确实是整数时,DecodeIntegral才可以编译。因此,你必须确保不会通过编译器看到调用非整数XDecodeIntegral(即未被实例化),而不仅仅是运行时不出现。

考虑到函数DecodeIntegral可以很容易地成为一个静态成员,而不改变语义,你可以使用“委托给类”的技巧来实现这一点。将DecodeIntegral移到一个助手类中:

template <bool Integral>
struct Helper;

template <>
struct Helper<true>
{
  template <class X>
  static void Decode( X& x, const IOBuffer::ConstSPtr& buffer, size_t& index )
  {
    // Old code of DecodeIntegral() goes here
  }  
};

template <>
struct Helper<false>
{
  template <class X>
  static void Decode( X& x, const IOBuffer::ConstSPtr& buffer, size_t& index )
  {
    // Code for non-integral decoding goes here
  }
};

struct DecoderFunctor
{
    template <typename X>
    void operator()( X& x, const IOBuffer::ConstSPtr& buffer, size_t& index )
    {
        Helper<std::is_integral<X>::value>::Decode(x, buffer, index);
    }
};

根据评论要求添加

如果你需要多个判别器,可以在辅助程序中添加更多的 bool 模板参数。如果没有你需要的标准判别器,你可以编写自己的判别器。

(下面的示例假定判别器是互斥的 - 最多只有一个为真):

// Two-discriminator helper
template <bool Integral, bool Array>
struct Helper;

template <>
struct Helper<true, false>
{
  void Decode() { /* integral decode */ }
};

template <>
struct Helper<false, true>
{
  void Decode() { /* array decode */ }
};


// Custom predicate
template <class T>
struct IsStdArray : std::false_type
{};

template <class T, size_t N>
struct IsStdArray<std::array<T, N>> : std::true_type
{};

// Usage
struct DecoderFunctor
{
    template <typename X>
    void operator()( X& x, const IOBuffer::ConstSPtr& buffer, size_t& index )
    {
        Helper<
            std::is_integral<X>::value,
            IsStdArray<X>::value
        >::Decode(x, buffer, index);
    }
};

你是对的,我应该学会更多地从编译时角度思考,而不是在运行时处理这些情况。顺便问一下,有没有办法在编译时检测数据是否为std::array?(std::is_array在我的情况下不起作用) - Athanase
@Athanase 我扩展了答案。 - Angew is no longer proud of SO
模板技术真的很棒。这太神奇了 :)。 - Athanase

1

std::is_integral<X>::value的值为false,但这并不意味着下面的代码(DecodeIntegral(x,buffer,index);)会被“跳过”。编译器也会看到这行代码。因此,你目前有一个运行时条件,但实际上你想要一个编译时条件。

编辑:我只是想用一个简短的例子来编辑我的答案,但是Angew更快。请参见他的答案:)


感谢你的回答! - Athanase

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接