c++ - Forwarding to in-place constructor -
i have message class bit of pain work with, had construct message class, tell allocate space object , populate space either construction or memberwise.
i want make possible construct message object immediate, inline new of resulting object, simple syntax @ call site while ensuring copy elision.
#include <cstdint> typedef uint8_t id_t; enum class messageid { worldpeace }; class message { uint8_t* m_data; // current memory uint8_t m_localdata[64]; // upto 64 bytes. id_t m_messageid; size_t m_size; // amount of data used size_t m_capacity; // amount of space available // ... public: message(size_t requestsize, id_t messageid) : m_data(m_localdata) , m_messageid(messageid) , m_size(0), m_capacity(sizeof(m_localdata)) { grow(requestsize); } void grow(size_t newsize) { if (newsize > m_capacity) { m_data = realloc((m_data == m_localdata) ? nullptr : m_data, newsize); assert(m_data != nullptr); // system uses less brutal mem mgmt m_size = newsize; } } template<typename t> t* allocateptr() { size_t offset = size; grow(offset + sizeof(t)); return (t*)(m_data + offset); } #ifdef use_cpp11 template<typename t, typename args...> message(id_t messageid, args&&... args) : message(sizeof(t), messageid) { // know m_data points large enough buffer new ((t*)m_data) t (std::forward<args>(args)...); } #endif };
pre-c++11 had nasty macro, construct_in_place, did:
#define construct_in_place(message, typename, ...) \ new ((message).allocateptr<typename>()) typename (__va_args__)
and say:
message outgoing(sizeof(mystruct), messageid::worldpeace); construct_in_place(outgoing, mystruct, wparg1, wparg2, wparg3);
with c++11, use
message outgoing<mystruct>(messageid::worldpeace, wparg1, wparg2, wparg3);
but find messy. want implement is:
template<typename t> message(id_t messageid, t&& src) : message(sizeof(t), messageid) { // know m_data points large enough buffer new ((t*)m_data) t (src); }
so user uses
message outgoing(messageid::worldpeace, mystruct(wparg1, wparg2, wparg3));
but seems first constructs temporary mystruct
on stack turning in-place new
call move constructor of t.
many of these messages simple, pod, , in marshalling functions this:
void dispatchworldpeace(int wparg1, int wparg2, int wparg3) { message outgoing(messageid::worldpeace, mystruct(wparg1, wparg2, wparg3)); outgoing.send(g_listener); }
so want avoid creating intermediate temporary going require subsequent move/copy.
it seems compiler should able eliminate temporary , move , forward construction way down in-place new
.
what doing causing not to? (gcc 4.8.1, clang 3.5, msvc 2013)
you won't able elide copy/move in placement new: copy elision entirely based on idea compiler knows @ construction time object end up. also, since copy elision changes behavior of program (after all, won't call respective constructor , destructor if have side-effects) copy elision limited few specific cases (listed in 12.8 [class.copy] paragraph 31: when returning local variable name, when throwing local variable name, when catching exception of correct type value, , when copying/moving temporary variable; see clause exact details). since [placement] new
none of contexts copy can elided , argument constructor not temporary (it named), copy/move never elided. adding missing std::forward<t>(...)
constructor cause copy/move elided:
template<typename t> message(id_t messageid, t&& src) : message(sizeof(t), messageid) { // placement new take void* anyway, i.e., no need cast new (m_data) t (std::forward<t>(src)); }
i don't think can explicitly specify template parameter when calling constructor. thus, think closest without constructing object ahead of time , getting copied/moved this:
template <typename> struct tag {}; template <typename t, typename a> message::message(tag<t>, id_t messageid, a... args) : message(messageid, sizeof(t)) { new(this->m_data) t(std::forward<a>(args)...); }
one approach might make things bit nicer using id_t
map relevant type assuming there mapping message ids relevant type:
typedef uint8_t id_t; template <typename t, id_t id> struct tag {}; struct messageid { static constexpr tag<mystruct, 1> worldpeace; // ... }; template <typename t, id_t id, typename... a> message::message(tag<t, id>, a&&... args) message(id, sizeof(t)) { new(this->m_data) t(std::forward<a>)(args)...); }
Comments
Post a Comment