Forcing type conversion in C++ using SFINAE -
i writing class (bufferinserter) transparently translates user defined messages network endian format , packs result user-supplied buffer. here simple illustration of message , 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 { homecoming message(val >> 8 | val << 8); } otamessage(const message& m) : val(val >> 8 | val << 8) {} };
here highly simplified version of 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; homecoming buf; } private: char* buf; };
the hope user like:
message m(1); char buf[256]; bufferinserter ins(buf); ins.insertstruct(m);
and c++ type deduction machinery skip passing native message insertstruct because message not have nativetype typedef, , instead convert message otamessage. that's not happens, instead compiler error (g++4.7)
test.cpp:55:23: error: no matching function phone call ‘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 here test.cpp:34:11: error: no type named ‘nativetype’ in ‘struct message’
there similar error msvc 2013 not compiler bug.
obviously works:
message m(1); char buf[256]; bufferinserter ins(buf); ins.insertstruct(otamessage(m));
but wanted avoid user having know byte swapping. add together conversion operator otamessage in message struct:
struct message { short val; message(short v): val(v){} operator otamessage() { val = v<<8 | v>>8; } };
there 2 issues this:
again, don't want user aware of otamessage otamessage defined after message message-->otamessage not possible. interestingly after trying in msvc 2013, crashed compiler:1>source.cpp(74): fatal error c1001: internal error has occurred in compiler. 1> (compiler file 'msc1.cpp', line 1325) 1> work around problem, seek simplifying or changing programme near locations listed above.
any help?
that's not how type deduction works. perhaps implement traits-like approach, letting bufferinserter::insertstruct<message>
deduced, , convert traits class. key pieces of might this:
template <typename t> struct otaconverter { // using ota_type = t; -- don't provide base of operations 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; homecoming buf; }
you'll need prepare otamessage
constructor not reference m.val
. safety should mark constructors explicit.
see live example. note removing specialization of otaconverter
results in intentional compilation errors.
c++ sfinae
No comments:
Post a Comment