Wednesday, 15 April 2015

Forcing type conversion in C++ using SFINAE -



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