Last active
August 29, 2015 14:03
-
-
Save cjhanks/52dded40719f8c69fafd to your computer and use it in GitHub Desktop.
Variadic Seriaization 2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "serial.hpp" | |
#include <iostream> | |
int | |
test(double a, int b) { | |
return a * b; | |
} | |
int | |
main() | |
{ | |
using namespace std; | |
auto resA = rpc::RPC(test).call(4, 3); | |
//auto resB = rpc::RPC(test).apply(resA); | |
//cerr << resA << endl; | |
//cerr << resB << endl; | |
for (size_t i = 0; i < 1000000; ++i) | |
rpc::RPC(test).exec(4, 3); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __SERIAL_HPP | |
#define __SERIAL_HPP | |
//////////////////////////////////////////////////////////////////////////////// | |
// | |
//////////////////////////////////////////////////////////////////////////////// | |
#include <cassert> | |
#include <cstddef> | |
#include <cstring> | |
#include <algorithm> | |
#include <functional> | |
#include <iomanip> | |
#include <iostream> | |
#include <map> | |
#include <tuple> | |
#include <utility> | |
#include <vector> | |
namespace rpc { | |
//////////////////////////////////////////////////////////////////////////////// | |
// | |
//------------------------------------------------------------------------------ | |
//////////////////////////////////////////////////////////////////////////////// | |
class SerializedPack { | |
struct Element { | |
Element(size_t offset, size_t size) | |
: offset(offset), size(size) | |
{} | |
bool | |
operator==(const Element& rhs) const | |
{ | |
return rhs.offset == offset | |
&& rhs.size == size; | |
} | |
size_t offset; | |
size_t size; | |
}; | |
public: | |
static SerializedPack | |
FromSerial(const std::string& serial) | |
{ | |
SerializedPack p; | |
size_t offset = 0; | |
std::transform(serial.begin(), serial.end() | |
, std::back_inserter(p.elementData) | |
, [](char c) { return static_cast<uint8_t>(c); }); | |
while (offset < serial.size()) { | |
uint32_t size = *reinterpret_cast<uint32_t*>( | |
&p.elementData[offset]); | |
offset += sizeof(size); | |
p.elementVector.emplace_back(Element(offset, size)); | |
offset += size; | |
} | |
return p; | |
} | |
bool | |
operator==(const SerializedPack& rhs) const | |
{ | |
return rhs.elementVector == elementVector | |
&& rhs.elementData == elementData; | |
} | |
void | |
addData(uint8_t* data, uint32_t len) | |
{ | |
const size_t offset = elementData.size(); | |
elementData.resize(elementData.size() + sizeof(len)); | |
std::memcpy(&elementData[offset] | |
, &len | |
, sizeof(len)); | |
elementVector.emplace_back(Element(elementData.size(), len)); | |
std::copy(data | |
, data + len | |
, std::back_inserter(elementData)); | |
} | |
void | |
addData(const std::string& data) | |
{ | |
const size_t offset = elementData.size(); | |
const uint32_t len = data.size(); | |
elementData.resize(elementData.size() + sizeof(len)); | |
std::memcpy(&elementData[offset] | |
, &len | |
, sizeof(len)); | |
elementVector.emplace_back(Element(elementData.size(), data.size())); | |
std::copy(data.begin() | |
, data.end() | |
, std::back_inserter(elementData)); | |
} | |
template <typename _Tp> | |
typename std::enable_if<std::is_pod<_Tp>::value, void>::type | |
copyInto(size_t index, _Tp& elem, size_t) const | |
{ | |
const Element& record = elementVector[index]; | |
std::memcpy(&elem | |
, &elementData[record.offset] | |
, record.size); | |
} | |
template <typename _Tp> | |
typename std::enable_if<!std::is_pod<_Tp>::value, void>::type | |
copyInto(size_t index, _Tp& elem, size_t) const | |
{ | |
using ElemType = typename _Tp::value_type; | |
const Element& record = elementVector[index]; | |
elem.resize(record.size / sizeof(ElemType)); | |
std::memcpy(&elem[0] | |
, &elementData[record.offset] | |
, record.size); | |
} | |
size_t | |
size() const | |
{ | |
return elementVector.size(); | |
} | |
std::string | |
serialize() const | |
{ | |
return std::string(elementData.begin(), elementData.end()); | |
} | |
friend std::ostream& operator<<(std::ostream&, SerializedPack); | |
private: | |
std::vector<Element> elementVector; | |
std::vector<uint8_t> elementData; | |
}; | |
std::ostream& | |
operator<<(std::ostream& strm, SerializedPack p) | |
{ | |
strm << "SerializedPack<" | |
<< p.elementVector.size() << ", " | |
<< p.elementData.size() << ">" | |
<< std::endl; | |
for (size_t i = 0; i < p.elementVector.size(); ++i) { | |
strm << std::setw(2) << i | |
<< ": " | |
<< std::setw(3) << p.elementVector[i].offset | |
<< "[" << p.elementVector[i].size << "]" | |
<< std::endl; | |
} | |
return strm; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// packedVector and implPackedVector | |
//------------------------------------------------------------------------------ | |
// These functions unpack a set of variadic arguments in to a a SerializedPack | |
// class. | |
//////////////////////////////////////////////////////////////////////////////// | |
// Forward declare the prototypical function | |
template <typename _Elem, typename... _Args> | |
void | |
implPackedVector(SerializedPack& data, _Elem elem, _Args... args); | |
// variadic sentinal | |
template <typename... _Args> | |
void | |
implPackedVector(SerializedPack&) | |
{ | |
return; | |
} | |
// string specialization: 1 | |
template <typename... _Args> | |
void | |
implPackedVector(SerializedPack& data, const std::string& elem, | |
_Args... args) | |
{ | |
data.addData(elem); | |
implPackedVector(data, args...); | |
} | |
// string specialization: 2 | |
template <typename... _Args> | |
void | |
implPackedVector(SerializedPack& data, const char* elem, | |
_Args... args) | |
{ | |
implPackedVector(data, std::string(elem), args...); | |
} | |
// vector specialization | |
template <typename _Elem, typename... _Args> | |
void | |
implPackedVector(SerializedPack& data, std::vector<_Elem> elem, | |
_Args... args) | |
{ | |
data.addData(reinterpret_cast<uint8_t*>(&elem[0]) | |
, elem.size() * sizeof(_Elem)); | |
implPackedVector(data, args...); | |
} | |
template <typename _Elem, typename... _Args> | |
void | |
implPackedVector(SerializedPack& data, _Elem elem, _Args... args) | |
{ | |
data.addData(reinterpret_cast<uint8_t*>(&elem) | |
, sizeof(_Elem)); | |
implPackedVector(data, args...); | |
} | |
template <typename... _Args> | |
SerializedPack | |
packedVector(_Args... args) | |
{ | |
SerializedPack pack; | |
implPackedVector(pack, args...); | |
return pack; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// Bind & Apply | |
//------------------------------------------------------------------------------ | |
// The SerializedPack is implicitly known to have an identical argument list as | |
// a functor to which it should be applied. This template pattern applies | |
// traverses down the SerializedPack list in reverse, at each stage it | |
// identifies the typename from a type_list constructed from the functor | |
// prototype signature. Once it has built up the a variadic list of arguments | |
// (ie _Size = 0) it applies them to the functor and returns the result from the | |
// original apply. | |
// | |
// If the SerializedPack does not contain enough elements, this will likely | |
// segfault. | |
// If the SerializedPack contains too many elements, it will apply the first N | |
// elements. | |
// If the function argument does not match the SerializedPack, behavior is | |
// undefined. | |
// | |
// All protection should be implemented /outside/ of this template. | |
//////////////////////////////////////////////////////////////////////////////// | |
template <typename _Functor, typename _RetType, typename _Tuple | |
, size_t _Size> | |
struct Bind { | |
template <typename... _Args> | |
static _RetType | |
apply(_Functor& functor, const SerializedPack& pack, _Args... args) | |
{ | |
constexpr size_t Indexed = _Size - 1; | |
using Type = typename std::tuple_element<Indexed, _Tuple>::type; | |
Type elem; | |
pack.copyInto(Indexed | |
, elem | |
, sizeof(elem)); | |
return Bind<_Functor | |
, _RetType | |
, _Tuple | |
, _Size - 1>::apply(functor, pack, elem, args...); | |
} | |
}; | |
template <typename _Functor, typename _RetType, typename _Tuple> | |
struct Bind<_Functor, _RetType, _Tuple, 0> { | |
template <typename... _Args> | |
static _RetType | |
apply(_Functor& functor, const SerializedPack& pack, _Args... args) | |
{ | |
return functor(args...); | |
} | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// RpcFunctor | |
//------------------------------------------------------------------------------ | |
//////////////////////////////////////////////////////////////////////////////// | |
class RpcFunctorAbstract { | |
public: | |
virtual SerializedPack | |
apply(const SerializedPack&) = 0; | |
}; | |
template <typename _RetType, typename... _Args> | |
class RpcFunctor : public RpcFunctorAbstract { | |
public: | |
using RetType = _RetType; | |
using Functor = std::function<_RetType(_Args...)>; | |
using Tuple = std::tuple<_Args...>; | |
RpcFunctor(Functor functor) | |
: functor(functor) {} | |
RetType | |
exec(_Args... args) | |
{ | |
return functor(args...); | |
} | |
SerializedPack | |
call(_Args... args) | |
{ | |
return packedVector(args...); | |
} | |
SerializedPack | |
apply(const SerializedPack& serial) | |
{ | |
return packedVector(Bind<Functor | |
, RetType | |
, Tuple | |
, sizeof...(_Args)>::apply(functor, serial)); | |
} | |
private: | |
Functor functor; | |
}; | |
/// {@ | |
template <typename _RetType, typename... _Args> | |
RpcFunctor<_RetType, _Args...> | |
RPC(_RetType (*functor)(_Args...)) { | |
return RpcFunctor<_RetType, _Args...>(functor); | |
} | |
template <typename _RetType, typename... _Args> | |
RpcFunctor<_RetType, _Args...> | |
RPC(std::function<_RetType(_Args...)> functor) { | |
return RpcFunctor<_RetType, _Args...>(functor); | |
} | |
/// @} | |
} // ns rpc | |
#endif //__SERIAL_HPP |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment