From: Peter Iannucci Date: Mon, 30 Sep 2013 18:10:03 +0000 (-0400) Subject: More marshalling simplification X-Git-Url: http://xvm.mit.edu/gitweb/invirt/third/libt4.git/commitdiff_plain/be7cf844f59fa483423724e8e4b5e663e5b88ddd More marshalling simplification --- diff --git a/Makefile b/Makefile index 010818d..5c2080b 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ EXTRA_TARGETS ?= all: lock_demo lock_server lock_tester rsm_tester rpc/rpctest $(EXTRA_TARGETS) -rpc/librpc.a: rpc/rpc.o rpc/marshall.o rpc/connection.o rpc/pollmgr.o rpc/thr_pool.o +rpc/librpc.a: rpc/rpc.o rpc/connection.o rpc/pollmgr.o rpc/thr_pool.o rm -f $@ ar cq $@ $^ ranlib rpc/librpc.a diff --git a/rpc/connection.cc b/rpc/connection.cc index c16f6dc..4681ae9 100644 --- a/rpc/connection.cc +++ b/rpc/connection.cc @@ -171,10 +171,6 @@ bool connection::writepdu() { if (wpdu_.solong == wpdu_.buf.size()) return true; - if (wpdu_.solong == 0) { - rpc_sz_t sz = hton((rpc_sz_t)(wpdu_.buf.size() - sizeof(uint32_t))); - copy((const char *)&sz, (const char *)(&sz+1), &wpdu_.buf[0]); - } ssize_t n = write(fd_, &wpdu_.buf[wpdu_.solong], (wpdu_.buf.size()-wpdu_.solong)); if (n < 0) { if (errno != EAGAIN) { @@ -219,7 +215,6 @@ bool connection::readpdu() { VERIFY(rpdu_.buf.size() == 0); rpdu_.buf = string(sz+sizeof(sz1), 0); - copy((const char *)&sz1, (const char *)(&sz1 + 1), &rpdu_.buf[0]); rpdu_.solong = sizeof(sz1); } diff --git a/rpc/marshall.cc b/rpc/marshall.cc deleted file mode 100644 index b8371cf..0000000 --- a/rpc/marshall.cc +++ /dev/null @@ -1,28 +0,0 @@ -#include "types.h" -#include "marshall.h" - -MARSHALL_RAW_NETWORK_ORDER_AS(bool, uint8_t) -MARSHALL_RAW_NETWORK_ORDER(uint8_t) -MARSHALL_RAW_NETWORK_ORDER(int8_t) -MARSHALL_RAW_NETWORK_ORDER(uint16_t) -MARSHALL_RAW_NETWORK_ORDER(int16_t) -MARSHALL_RAW_NETWORK_ORDER(uint32_t) -MARSHALL_RAW_NETWORK_ORDER(int32_t) -MARSHALL_RAW_NETWORK_ORDER_AS(size_t, uint32_t) -MARSHALL_RAW_NETWORK_ORDER(uint64_t) -MARSHALL_RAW_NETWORK_ORDER(int64_t) - -marshall & operator<<(marshall &m, const string &s) { - m << (uint32_t)s.size(); - m.rawbytes(s.data(), s.size()); - return m; -} - -unmarshall & operator>>(unmarshall &u, string &s) { - uint32_t sz = u.grab(); - if (u.ok()) { - s.resize(sz); - u.rawbytes(&s[0], sz); - } - return u; -} diff --git a/rpc/marshall.h b/rpc/marshall.h index ecf16f7..0e0af8d 100644 --- a/rpc/marshall.h +++ b/rpc/marshall.h @@ -4,43 +4,18 @@ #include "types.h" #include "rpc_protocol.h" -// for structs or classes containing a MEMBERS declaration class marshall; class unmarshall; -#define FORWARD_MARSHALLABLE(_c_) \ -extern unmarshall & operator>>(unmarshall &u, typename remove_reference<_c_>::type &a); \ -extern marshall & operator<<(marshall &m, const _c_ a); -#define MARSHALLABLE(_c_) \ -inline unmarshall & operator>>(unmarshall &u, _c_ &a) { return u >> a._tuple_(); } \ -inline marshall & operator<<(marshall &m, const _c_ a) { return m << a._tuple_(); } - -// for plain old data -#define MARSHALL_RAW_NETWORK_ORDER_AS(_c_, _d_) \ -marshall & operator<<(marshall &m, _c_ x) { _d_ y = hton((_d_)x); m.rawbytes(&y, sizeof(_d_)); return m; } \ -unmarshall & operator>>(unmarshall &u, _c_ &x) { _d_ y; u.rawbytes(&y, sizeof(_d_)); x = (_c_)ntoh(y); return u; } -#define MARSHALL_RAW_NETWORK_ORDER(_c_) MARSHALL_RAW_NETWORK_ORDER_AS(_c_, _c_) - -FORWARD_MARSHALLABLE(request_header) -ENDIAN_SWAPPABLE(request_header) - -FORWARD_MARSHALLABLE(reply_header) -ENDIAN_SWAPPABLE(reply_header) - -// Template parameter pack expansion is not allowed in certain contexts, but -// brace initializers (for instance, calls to constructors of empty structs) -// are fair game. -struct pass { template inline pass(Args&&...) {} }; +// +// Marshall and unmarshall objects +// class marshall { private: string buf_ = string(DEFAULT_RPC_SZ, 0); // Raw bytes buffer size_t index_ = RPC_HEADER_SZ; // Read/write head position - inline void reserve(size_t n) { - if (index_+n > buf_.size()) - buf_.resize(index_+n); - } public: template marshall(const Args&... args) { @@ -48,75 +23,36 @@ class marshall { } void rawbytes(const void *p, size_t n) { - reserve(n); + if (index_+n > buf_.size()) + buf_.resize(index_+n); copy((char *)p, (char *)p+n, &buf_[index_]); index_ += n; } // with header - operator string () const { return buf_.substr(0,index_); } + inline operator string() const { return buf_.substr(0,index_); } // without header - string content() { return buf_.substr(RPC_HEADER_SZ,index_-RPC_HEADER_SZ); } - - template - void pack_header(const T &h) { - VERIFY(sizeof(T)+sizeof(rpc_sz_t) <= RPC_HEADER_SZ); + inline string content() { return buf_.substr(RPC_HEADER_SZ,index_-RPC_HEADER_SZ); } + + // letting S be a defaulted template parameter forces the compiler to + // delay looking up operator<<(marshall&, rpc_sz_t) until we define it + // (i.e. we define an operator for marshalling uint32_t) + template inline void + pack_header(const T & h) { + VERIFY(sizeof(T)+sizeof(S) <= RPC_HEADER_SZ); size_t saved_sz = index_; - index_ = sizeof(rpc_sz_t); // first 4 bytes hold length field - *this << h; + index_ = 0; + *this << (S)(saved_sz - sizeof(S)) << (T)h; index_ = saved_sz; } }; -FORWARD_MARSHALLABLE(bool); -FORWARD_MARSHALLABLE(uint8_t); -FORWARD_MARSHALLABLE(int8_t); -FORWARD_MARSHALLABLE(uint16_t); -FORWARD_MARSHALLABLE(int16_t); -FORWARD_MARSHALLABLE(uint32_t); -FORWARD_MARSHALLABLE(int32_t); -FORWARD_MARSHALLABLE(size_t); -FORWARD_MARSHALLABLE(uint64_t); -FORWARD_MARSHALLABLE(int64_t); -FORWARD_MARSHALLABLE(string &); - -template typename enable_if::value, marshall>::type & -operator<<(marshall &m, const A &x) { - m << (unsigned int)x.size(); - for (const auto &a : x) - m << a; - return m; -} - -template marshall & -operator<<(marshall &m, const pair &d) { - return m << d.first << d.second; -} - -template -using enum_type_t = typename enable_if::value, typename underlying_type::type>::type; -template constexpr inline enum_type_t from_enum(E e) noexcept { return (enum_type_t)e; } -template constexpr inline E to_enum(enum_type_t value) noexcept { return (E)value; } - -template typename enable_if::value, marshall>::type & -operator<<(marshall &m, E e) { - return m << from_enum(e); -} - -template typename enable_if::value, unmarshall>::type & -operator>>(unmarshall &u, E &e); - class unmarshall { private: string buf_; size_t index_ = 0; bool ok_ = false; - inline bool ensure(size_t n) { - if (index_+n > buf_.size()) - ok_ = false; - return ok_; - } public: unmarshall() {} unmarshall(const string &s, bool has_header) @@ -130,15 +66,17 @@ class unmarshall { bool okdone() const { return ok_ && index_ == buf_.size(); } void rawbytes(void * t, size_t n) { - VERIFY(ensure(n)); + if (index_+n > buf_.size()) + ok_ = false; + VERIFY(ok_); copy(&buf_[index_], &buf_[index_+n], (char *)t); index_ += n; } - template - void unpack_header(T & h) { - // first 4 bytes hold length field + template void + unpack_header(T & h) { VERIFY(sizeof(T)+sizeof(rpc_sz_t) <= RPC_HEADER_SZ); + // first 4 bytes hold length field index_ = sizeof(rpc_sz_t); *this >> h; index_ = RPC_HEADER_SZ; @@ -147,181 +85,154 @@ class unmarshall { template inline T grab() { T t; *this >> t; return t; } }; -template typename enable_if::value, unmarshall>::type & -operator>>(unmarshall &u, A &x) { - unsigned n = u.grab(); - x.clear(); - while (n--) - x.emplace_back(u.grab()); - return u; -} +// +// Marshalling for plain old data +// -template unmarshall & -operator>>(unmarshall &u, map &x) { - unsigned n = u.grab(); - x.clear(); - while (n--) - x.emplace(u.grab>()); - return u; +#define MARSHALL_RAW_NETWORK_ORDER_AS(_c_, _d_) \ +inline marshall & operator<<(marshall &m, _c_ x) { _d_ y = hton((_d_)x); m.rawbytes(&y, sizeof(_d_)); return m; } \ +inline unmarshall & operator>>(unmarshall &u, _c_ &x) { _d_ y; u.rawbytes(&y, sizeof(_d_)); x = (_c_)ntoh(y); return u; } + +#define MARSHALL_RAW_NETWORK_ORDER(_c_) MARSHALL_RAW_NETWORK_ORDER_AS(_c_, _c_) + +MARSHALL_RAW_NETWORK_ORDER_AS(bool, uint8_t) +MARSHALL_RAW_NETWORK_ORDER(uint8_t) +MARSHALL_RAW_NETWORK_ORDER(int8_t) +MARSHALL_RAW_NETWORK_ORDER(uint16_t) +MARSHALL_RAW_NETWORK_ORDER(int16_t) +MARSHALL_RAW_NETWORK_ORDER(uint32_t) +MARSHALL_RAW_NETWORK_ORDER(int32_t) +MARSHALL_RAW_NETWORK_ORDER_AS(size_t, uint32_t) +MARSHALL_RAW_NETWORK_ORDER(uint64_t) +MARSHALL_RAW_NETWORK_ORDER(int64_t) + +// +// Marshalling for tuples (used to implement marshalling for structs) +// + +// In order to iterate over the tuple elements, we first need a template +// parameter pack containing the tuple's indices. The function templates named +// *_imp below accept an empty tag struct as their last argument, and use its +// template arguments to index the tuple. The operator<< overloads instantiate +// the appropriate tag struct to make this possible. + +template inline marshall & +tuple_marshall_imp(marshall & m, tuple & t, tuple_indices) { + // Note that brace initialization is used for the empty structure "pack", + // forcing the comma-separated expressions expanded from the parameter pack + // to be evaluated in order. Order matters because the elements must be + // serialized consistently! The empty struct resulting from construction + // is discarded. + (void)pass{(m << get(t))...}; + return m; } -template unmarshall & -operator>>(unmarshall &u, pair &d) { - return u >> d.first >> d.second; +template marshall & +operator<<(marshall & m, tuple && t) { + using Indices = typename make_tuple_indices::type; + return tuple_marshall_imp(m, t, Indices()); } -template typename enable_if::value, unmarshall>::type & -operator>>(unmarshall &u, E &e) { - e = to_enum(u.grab>()); +template inline unmarshall & +tuple_unmarshall_imp(unmarshall & u, tuple t, tuple_indices) { + (void)pass{(u >> get(t))...}; return u; } -typedef function handler; +template unmarshall & +operator>>(unmarshall & u, tuple && t) { + using Indices = typename make_tuple_indices::type; + return tuple_unmarshall_imp(u, t, Indices()); +} // -// Automatic marshalling wrappers for RPC handlers +// Marshalling for structs or classes containing a MEMBERS declaration // -// PAI 2013/09/19 -// C++11 does neither of these two things for us: -// 1) Declare variables using a parameter pack expansion, like so -// Args... args; -// 2) Call a function with a tuple of the arguments it expects -// -// We implement an 'invoke' function for functions of the RPC handler -// signature, i.e. int(R & r, const Args...) -// -// One thing we need in order to accomplish this is a way to cause the compiler -// to specialize 'invoke' with a parameter pack containing a list of indices -// for the elements of the tuple. This will allow us to call the underlying -// function with the exploded contents of the tuple. The empty type -// tuple_indices accomplishes this. It will be passed in to -// 'invoke' as a parameter which will be ignored, but its type will force the -// compiler to specialize 'invoke' appropriately. - -// This class encapsulates the default response to runtime unmarshalling -// failures. The templated wrappers below may optionally use a different -// class. - -struct VerifyOnFailure { - static inline int unmarshall_args_failure() { - VERIFY(0); - return 0; - } -}; +// Implements struct marshalling via tuple marshalling of members. +#define MARSHALLABLE(_c_) \ +inline unmarshall & operator>>(unmarshall &u, _c_ &a) { return u >> a._tuple_(); } \ +inline marshall & operator<<(marshall &m, const _c_ a) { return m << a._tuple_(); } -// Here's the implementation of 'invoke'. It could be more general, but this -// meets our needs. +// our first two marshallable structs... +MARSHALLABLE(request_header) +MARSHALLABLE(reply_header) -// One for function pointers... +// +// Marshalling for STL containers +// -template -typename enable_if::value, RV>::type -invoke(RV, F f, void *, R & r, args_type & t, tuple_indices) { - return f(r, move(get(t))...); +// this overload is visible for type A only if A::cbegin and A::cend exist +template inline typename +enable_if::value, marshall>::type & +operator<<(marshall &m, const A &x) { + m << (unsigned int)x.size(); + for (const auto &a : x) + m << a; + return m; } -// And one for pointers to member functions... - -template -typename enable_if::value, RV>::type -invoke(RV, F f, C *c, R & r, args_type & t, tuple_indices) { - return (c->*f)(r, move(get(t))...); +// visible for type A if A::emplace_back(a) makes sense +template inline typename +enable_if::value, unmarshall>::type & +operator>>(unmarshall &u, A &x) { + unsigned n = u.grab(); + x.clear(); + while (n--) + x.emplace_back(u.grab()); + return u; } -// The class marshalled_func_imp uses partial template specialization to -// implement the ::wrap static function. ::wrap takes a function pointer or a -// pointer to a member function and returns a handler * object which -// unmarshalls arguments, verifies successful unmarshalling, calls the supplied -// function, and marshalls the response. - -template struct marshalled_func_imp; - -// Here we specialize on the Signature template parameter to obtain the list of -// argument types. Note that we do not assume that the Functor parameter has -// the same pattern as Signature; this allows us to ignore the distinctions -// between various types of callable objects at this level of abstraction. - -template -struct marshalled_func_imp { - static inline handler *wrap(F f, C *c=nullptr) { - // This type definition corresponds to an empty struct with - // template parameters running from 0 up to (# args) - 1. - using Indices = typename make_tuple_indices::type; - // This type definition represents storage for f's unmarshalled - // arguments. decay is (most notably) stripping off const - // qualifiers. - using ArgsStorage = tuple::type...>; - // Allocate a handler (i.e. function) to hold the lambda - // which will unmarshall RPCs and call f. - return new handler([=](unmarshall &u, marshall &m) -> RV { - // Unmarshall each argument with the correct type and store the - // result in a tuple. - ArgsStorage t = {u.grab::type>()...}; - // Verify successful unmarshalling of the entire input stream. - if (!u.okdone()) - return (RV)ErrorHandler::unmarshall_args_failure(); - // Allocate space for the RPC response -- will be passed into the - // function as an lvalue reference. - R r; - // Perform the invocation. Note that Indices() calls the default - // constructor of the empty struct with the special template - // parameters. - RV b = invoke(RV(), f, c, r, t, Indices()); - // Marshall the response. - m << r; - // Make like a tree. - return b; - }); - } -}; - -// More partial template specialization shenanigans to reduce the number of -// parameters which must be provided explicitly and to support a few common -// callable types. C++11 doesn't allow partial function template -// specialization, so we use classes (structs). - -template struct marshalled_func; - -template -struct marshalled_func : - public marshalled_func_imp {}; - -template -struct marshalled_func : - public marshalled_func_imp {}; +// std::pair +template inline marshall & +operator<<(marshall &m, const pair &d) { + return m << d.first << d.second; +} -template -struct marshalled_func> : - public marshalled_func_imp {}; +template inline unmarshall & +operator>>(unmarshall &u, pair &d) { + return u >> d.first >> d.second; +} -template unmarshall & -tuple_unmarshall_imp(unmarshall & u, tuple t, tuple_indices) { - (void)pass{(u >> get(t))...}; +// std::map +template inline unmarshall & +operator>>(unmarshall &u, map &x) { + unsigned n = u.grab(); + x.clear(); + while (n--) + x.emplace(u.grab>()); return u; } -template unmarshall & -operator>>(unmarshall & u, tuple && t) { - using Indices = typename make_tuple_indices::type; - return tuple_unmarshall_imp(u, t, Indices()); +// std::string +inline marshall & operator<<(marshall &m, const string &s) { + m << (uint32_t)s.size(); + m.rawbytes(s.data(), s.size()); + return m; } -template marshall & -tuple_marshall_imp(marshall & m, tuple & t, tuple_indices) { - (void)pass{(m << get(t))...}; - return m; +inline unmarshall & operator>>(unmarshall &u, string &s) { + uint32_t sz = u.grab(); + if (u.ok()) { + s.resize(sz); + u.rawbytes(&s[0], sz); + } + return u; } -template marshall & -operator<<(marshall & m, tuple && t) { - using Indices = typename make_tuple_indices::type; - return tuple_marshall_imp(m, t, Indices()); +// +// Marshalling for strongly-typed enums +// + +template typename enable_if::value, marshall>::type & +operator<<(marshall &m, E e) { + return m << from_enum(e); } -MARSHALLABLE(request_header) -MARSHALLABLE(reply_header) +template typename enable_if::value, unmarshall>::type & +operator>>(unmarshall &u, E &e) { + e = to_enum(u.grab>()); + return u; +} #endif diff --git a/rpc/marshall_wrap.h b/rpc/marshall_wrap.h new file mode 100644 index 0000000..2d7fbfb --- /dev/null +++ b/rpc/marshall_wrap.h @@ -0,0 +1,127 @@ +#ifndef marshall_wrap_h +#define marshall_wrap_h + +#include "marshall.h" + +typedef function handler; + +// +// Automatic marshalling wrappers for RPC handlers +// + +// PAI 2013/09/19 +// C++11 does neither of these two things for us: +// 1) Declare variables using a parameter pack expansion, like so +// Args... args; +// 2) Call a function with a tuple of the arguments it expects +// +// We implement an 'invoke' function for functions of the RPC handler +// signature, i.e. int(R & r, const Args...) +// +// One thing we need in order to accomplish this is a way to cause the compiler +// to specialize 'invoke' with a parameter pack containing a list of indices +// for the elements of the tuple. This will allow us to call the underlying +// function with the exploded contents of the tuple. The empty type +// tuple_indices accomplishes this. It will be passed in to +// 'invoke' as a parameter which will be ignored, but its type will force the +// compiler to specialize 'invoke' appropriately. + +// This class encapsulates the default response to runtime unmarshalling +// failures. The templated wrappers below may optionally use a different +// class. + +struct VerifyOnFailure { + static inline int unmarshall_args_failure() { + VERIFY(0); + return 0; + } +}; + +// Here's the implementation of 'invoke'. It could be more general, but this +// meets our needs. + +// One for function pointers... + +template +typename enable_if::value, RV>::type +invoke(RV, F f, void *, R & r, args_type & t, tuple_indices) { + return f(r, move(get(t))...); +} + +// And one for pointers to member functions... + +template +typename enable_if::value, RV>::type +invoke(RV, F f, C *c, R & r, args_type & t, tuple_indices) { + return (c->*f)(r, move(get(t))...); +} + +// The class marshalled_func_imp uses partial template specialization to +// implement the ::wrap static function. ::wrap takes a function pointer or a +// pointer to a member function and returns a handler * object which +// unmarshalls arguments, verifies successful unmarshalling, calls the supplied +// function, and marshalls the response. + +template struct marshalled_func_imp; + +// Here we specialize on the Signature template parameter to obtain the list of +// argument types. Note that we do not assume that the Functor parameter has +// the same pattern as Signature; this allows us to ignore the distinctions +// between various types of callable objects at this level of abstraction. + +template +struct marshalled_func_imp { + static inline handler *wrap(F f, C *c=nullptr) { + // This type definition corresponds to an empty struct with + // template parameters running from 0 up to (# args) - 1. + using Indices = typename make_tuple_indices::type; + // This type definition represents storage for f's unmarshalled + // arguments. decay is (most notably) stripping off const + // qualifiers. + using ArgsStorage = tuple::type...>; + // Allocate a handler (i.e. function) to hold the lambda + // which will unmarshall RPCs and call f. + return new handler([=](unmarshall &u, marshall &m) -> RV { + // Unmarshall each argument with the correct type and store the + // result in a tuple. + ArgsStorage t = {u.grab::type>()...}; + // Verify successful unmarshalling of the entire input stream. + if (!u.okdone()) + return (RV)ErrorHandler::unmarshall_args_failure(); + // Allocate space for the RPC response -- will be passed into the + // function as an lvalue reference. + R r; + // Perform the invocation. Note that Indices() calls the default + // constructor of the empty struct with the special template + // parameters. + RV b = invoke(RV(), f, c, r, t, Indices()); + // Marshall the response. + m << r; + // Make like a tree. + return b; + }); + } +}; + +// More partial template specialization shenanigans to reduce the number of +// parameters which must be provided explicitly and to support a few common +// callable types. C++11 doesn't allow partial function template +// specialization, so we use classes (structs). + +template struct marshalled_func; + +template +struct marshalled_func : + public marshalled_func_imp {}; + +template +struct marshalled_func : + public marshalled_func_imp {}; + +template +struct marshalled_func> : + public marshalled_func_imp {}; + +#endif diff --git a/rpc/rpc.h b/rpc/rpc.h index 2b32e28..065cabc 100644 --- a/rpc/rpc.h +++ b/rpc/rpc.h @@ -7,6 +7,7 @@ #include "thr_pool.h" #include "marshall.h" +#include "marshall_wrap.h" #include "connection.h" class rpc_const { diff --git a/rpc/rpc_protocol.h b/rpc/rpc_protocol.h index 2ef9ab2..8107f04 100644 --- a/rpc/rpc_protocol.h +++ b/rpc/rpc_protocol.h @@ -17,6 +17,8 @@ struct request_header { MEMBERS(xid, proc, clt_nonce, srv_nonce, xid_rep) }; +ENDIAN_SWAPPABLE(request_header) + struct reply_header { int xid; int ret; @@ -24,6 +26,8 @@ struct reply_header { MEMBERS(xid, ret) }; +ENDIAN_SWAPPABLE(reply_header) + const size_t RPC_HEADER_SZ = max(sizeof(request_header), sizeof(reply_header)) + sizeof(rpc_sz_t); const size_t DEFAULT_RPC_SZ = 1024; // size of initial buffer allocation const size_t MAX_PDU = 10<<20; //maximum PDF is 10M diff --git a/threaded_log.h b/threaded_log.h index 750c5d2..5d3942c 100644 --- a/threaded_log.h +++ b/threaded_log.h @@ -19,7 +19,7 @@ namespace std { } template -typename enable_if::value && !is_same::value, ostream>::type & +typename enable_if::value && !is_same::value, ostream>::type & operator<<(ostream &o, const A &a) { return o << "[" << implode(a, ", ") << "]"; } diff --git a/types.h b/types.h index e78186c..cdb629a 100644 --- a/types.h +++ b/types.h @@ -99,6 +99,7 @@ using std::is_same; using std::underlying_type; using std::enable_if; using std::remove_reference; +using std::add_const; #include using std::pair; @@ -108,15 +109,29 @@ using std::forward; #include using std::vector; +// type traits and manipulators -template struct is_iterable : false_type {}; +template struct is_const_iterable : false_type {}; -template struct is_iterable struct is_const_iterable().cbegin(), declval().cend(), void()) > : true_type {}; +template struct supports_emplace_back : false_type {}; + +template struct supports_emplace_back().emplace_back(declval()), void()) +> : true_type {}; + +template +using enum_type_t = typename enable_if::value, typename underlying_type::type>::type; +template constexpr inline enum_type_t from_enum(E e) noexcept { return (enum_type_t)e; } +template constexpr inline E to_enum(enum_type_t value) noexcept { return (E)value; } + +// string manipulation + template -inline typename enable_if::value, string>::type +inline typename enable_if::value, string>::type implode(const C & v, string delim=" ") { if (v.begin() == v.end()) return string(); @@ -141,10 +156,25 @@ inline vector explode(const string &s, string delim=" ") { #include "lang/verify.h" #include "threaded_log.h" +// struct tuple adapter, useful for marshalling +// used like +// struct foo { +// int a, b; +// MEMBERS(a, b) +// }; + #define MEMBERS(...) \ inline auto _tuple_() -> decltype(tie(__VA_ARGS__)) { return tie(__VA_ARGS__); } \ inline auto _tuple_() const -> decltype(tie(__VA_ARGS__)) { return tie(__VA_ARGS__); } +// struct ordering and comparison +// used like +// struct foo { +// int a, b; +// MEMBERS(a, b) +// }; +// LEXICOGRAPHIC_COMPARISON(foo) + #define LEXICOGRAPHIC_OPERATOR(_c_, _op_) \ inline bool operator _op_(const _c_ &b) const { return _tuple_() _op_ b._tuple_(); } @@ -153,7 +183,9 @@ LEXICOGRAPHIC_OPERATOR(_c_, <) LEXICOGRAPHIC_OPERATOR(_c_, <=) \ LEXICOGRAPHIC_OPERATOR(_c_, >) LEXICOGRAPHIC_OPERATOR(_c_, >=) \ LEXICOGRAPHIC_OPERATOR(_c_, ==) LEXICOGRAPHIC_OPERATOR(_c_, !=) -// The following implementation of tuple_indices is redistributed under the MIT +// crucial tool for tuple indexing in variadic templates +// +// This implementation of tuple_indices is redistributed under the MIT // License as an insubstantial portion of the LLVM compiler infrastructure. template struct tuple_indices {}; @@ -168,6 +200,11 @@ template struct make_tuple_indices { typedef typename make_indices_imp, E>::type type; }; +// Template parameter pack expansion is not allowed in certain contexts, but +// brace initializers (for instance, calls to constructors of empty structs) +// are fair game. +struct pass { template inline pass(Args&&...) {} }; + #include "endian.h" #endif