简体   繁体   English

使用SFINAE在C ++中强制类型转换

[英]Forcing type conversion in C++ using SFINAE

I am writing a class (BufferInserter) that transparently translates user defined messages to their network endian format and packs the result into a user-supplied buffer. 我正在编写一个类(BufferInserter),该类将用户定义的消息透明地转换为他们的网络字节序格式,并将结果打包到用户提供的缓冲区中。 Here is a simple example of a Message and its byte-swapped counterpart: 这是一条消息及其字节交换对象的简单示例:

//Native message (not in network endian format)
struct Message
{
    short val;
    Message(short v): val(v){}
};

//Network endian format of Message
struct OtaMessage
{
    typedef Message NativeType;
    short val;

    operator Message() const
    {
        return Message(val >> 8 | val << 8); 
    }

    OtaMessage(const Message& m)
        : val(val >> 8 | val << 8)
    {}
};

Here is a highly simplified version of BufferInserter: 这是BufferInserter的高度简化版本:

class BufferInserter
{
public:
    BufferInserter(char* buffer)
        :buf(buffer)
    {}

    template<typename T>
    char* insertStruct(T s, typename T::NativeType = 0)
    {
        const std::size_t size = sizeof(T);
        *reinterpret_cast<T*>(buf) = s;
        buf += size;
        return buf;
    }
private:
    char* buf;
};

The hope was a user could do something like: 希望用户可以执行以下操作:

Message m(1);
char buf[256];
BufferInserter ins(buf);
ins.insertStruct(m);

And the C++ type deduction machinery would skip passing the native Message to insertStruct because Message does not have a NativeType typedef, and instead would convert the Message to a OtaMessage. C ++类型推导机制会跳过将本机Message传递给insertStruct的原因,因为Message没有NativeType typedef,而是将Me​​ssage转换为OtaMessage。 That's not what happens, instead I get the compiler error (g++4.7) 那不是发生了什么,而是出现编译器错误(g ++ 4.7)

test.cpp:55:23: error: no matching function for call to ‘BufferInserter::insertStruct(Message&)’
     ins.insertStruct(m);
                       ^
test.cpp:55:23: note: candidate is:
test.cpp:34:11: note: template<class T> char* BufferInserter::insertStruct(T, typename T::NativeType)
     char* insertStruct(T s, typename T::NativeType = 0)
           ^
test.cpp:34:11: note:   template argument deduction/substitution failed:
test.cpp: In substitution of ‘template<class T> char* BufferInserter::insertStruct(T, typename T::NativeType) [with T = Message]’:
test.cpp:55:23:   required from here
test.cpp:34:11: error: no type named ‘NativeType’ in ‘struct Message’

There is a similar error for MSVC 2013 so its probably not a compiler bug. MSVC 2013存在类似错误,因此它可能不是编译器错误。

Obviously this works: 显然,这可行:

Message m(1);
char buf[256];
BufferInserter ins(buf);
ins.insertStruct(OtaMessage(m));

But I wanted to avoid the user having to know about the byte swapping. 但是我想避免用户必须了解字节交换。 I also could add a conversion operator to OtaMessage in the Message struct: 我还可以在Message结构中向OtaMessage添加一个转换运算符:

struct Message
{
    short val;
    Message(short v): val(v){}
    operator OtaMessage()
    {
       val = v<<8 | v>>8;
    }
};

There are 2 issues with this: 有两个问题:

  1. Again, I don't want the user to be aware of OtaMessage 同样,我不希望用户意识到OtaMessage
  2. OtaMessage is defined after Message so a Message-->OtaMessage is not possible. OtaMessage是在Message之后定义的,因此无法使用Message-> OtaMessage。 Interestingly after trying this in MSVC 2013, I crashed the compiler: 有趣的是,在MSVC 2013中尝试此操作后,我使编译器崩溃:

1>Source.cpp(74): fatal error C1001: An internal error has occurred in the compiler. 1> Source.cpp(74):致命错误C1001:编译器发生内部错误。 1> (compiler file 'msc1.cpp', line 1325) 1> To work around this problem, try simplifying or changing the program near the locations listed above. 1>(编译器文件'msc1.cpp',第1325行)1>要变通解决此问题,请尝试简化或更改上述位置附近的程序。

Any help? 有什么帮助吗?

That's not how type deduction works. 那不是类型推导的原理。 Perhaps you could implement a traits-like approach, letting BufferInserter::insertStruct<Message> be deduced, and then convert with your traits class. 也许您可以实现类似特征的方法,让BufferInserter::insertStruct<Message>推导,然后与特征类一起转换。 Some key pieces of it might look like this: 它的一些关键部分可能看起来像这样:

template <typename T>
struct OtaConverter {
  // using ota_type = T; -- don't provide a base ota_type
};

: : :

template <> struct OtaConverter<Message> {
  using ota_type = OtaMessage;
};

: : :

template<typename T>
char* BufferInserter::insertStruct(T s, typename OtaConverter<T>::ota_type* = 0)
{
    using OT = typename OtaConverter<T>::ota_type;
    const std::size_t size = sizeof(OT);
    *reinterpret_cast<OT*>(buf) = OT(s);
    buf += size;
    return buf;
}

You'll also need to fix your OtaMessage constructor as it currently does not reference m.val . 您还需要修复OtaMessage构造函数,因为它目前未引用m.val For safety you should also mark your constructors explicit. 为了安全起见,还应该将构造函数标记为显式。

See this live example . 请参见此示例 Note that removing the specialization of OtaConverter currently results in intentional compilation errors. 请注意,删除OtaConverter的专业化当前会导致故意的编译错误。

I encounterd the same problem (in other circumstances) and I fixed it by updating my MSVC2013 Tools -> Extensions and Updates -> Updates -> Product Updates (error fixed since update 2 https://support.microsoft.com/fr-fr/kb/2927432 ). 我遇到了相同的问题(在其他情况下),并且通过更新MSVC2013工具->扩展和更新->更新->产品更新(自更新2开始已修复错误https://support.microsoft.com/fr-fr / kb / 2927432 )。

Cheers, 干杯,

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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