Skip to content

Instantly share code, notes, and snippets.

@cjhanks
Last active August 29, 2015 14:03
Show Gist options
  • Save cjhanks/52dded40719f8c69fafd to your computer and use it in GitHub Desktop.
Save cjhanks/52dded40719f8c69fafd to your computer and use it in GitHub Desktop.
Variadic Seriaization 2
#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);
}
#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