You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pEpJSONServerAdapter/server/inout.hh

300 lines
6.8 KiB
C++

#ifndef PEP_JSON_SERVER_INOUT_HH
#define PEP_JSON_SERVER_INOUT_HH
#include "json_spirit/json_spirit_value.h"
#include "context.hh"
#include <type_traits>
#include <stdexcept>
// Just for debugging:
#include <iostream>
// is a bitfield that controls the In<> / Out<> parameter types
enum class ParamFlag : unsigned
{
Default = 0,
NoInput = 1, // has no input parameter in the JSON API. Value comes from Context.
DontOwn = 2, // parameter holds a "shared resource". Don't free() it in destructor.
NullOkay = 4, // JSON 'null' value accepted, will become a C NULL pointer.
};
inline constexpr
ParamFlag operator|(ParamFlag a, ParamFlag b)
{
return ParamFlag( unsigned(a) | unsigned(b) );
}
inline constexpr
ParamFlag operator&(ParamFlag a, ParamFlag b)
{
return ParamFlag( unsigned(a) & unsigned(b) );
}
inline constexpr
bool operator!(ParamFlag pf)
{
return !unsigned(pf);
}
namespace js = json_spirit;
template<class T>
T from_json(const js::Value& v);
template<class T>
js::Value to_json(const T& t);
// common stuff in base class
template<class T, ParamFlag PF>
struct InBase
{
typedef T c_type; // the according type in C function parameter
enum { is_output = false, need_input = !(PF & ParamFlag::NoInput) };
InBase(const InBase<T,PF>& other) = delete;
InBase(InBase<T,PF>&& victim) = delete;
void operator=(const InBase<T,PF>&) = delete;
js::Value to_json() const
{
throw std::logic_error( std::string("Never call to_json() for a In<") + typeid(T).name() + "> data type! fn=" + __PRETTY_FUNCTION__ );
}
c_type get_value() const { return value; }
T value;
protected:
explicit InBase(T&& t) : value(t) {}
};
// helper classes to specify in- and out-parameters
template<class T, ParamFlag PF=ParamFlag::Default>
struct In : public InBase<T,PF>
{
typedef InBase<T,PF> Base;
~In();
// default implementation:
In(const js::Value& v, Context*, unsigned param_nr)
: Base( from_json<T>(v) )
{ }
};
template<class T>
struct In<T, ParamFlag::NoInput> : public InBase<T, ParamFlag::NoInput>
{
typedef InBase<T, ParamFlag::NoInput> Base;
~In() {}
// default implementation: ignore all parameters
In(const js::Value&, Context*, unsigned param_nr)
: Base{ T{} }
{ }
};
class JsonAdapter;
template<>
struct In<JsonAdapter*, ParamFlag::NoInput> : public InBase<JsonAdapter*, ParamFlag::NoInput>
{
typedef InBase<JsonAdapter*, ParamFlag::NoInput> Base;
~In() {}
// defined in json-adapter.cc
In(const js::Value&, Context*, unsigned param_nr);
};
// to call functions that operate directly on the JSON data type
template<class T, ParamFlag PF=ParamFlag::Default>
struct InRaw : public InBase<T, PF>
{
~InRaw() = default;
// default implementation:
InRaw(const js::Value& v, Context*, unsigned param_nr)
: InBase<T,PF>(v)
{ }
};
// helper classes to specify in- and out-parameters whose output is in-place.
// Use InOutP<T> for in/out parameters where the function might change the object and expects a pointer
template<class T, ParamFlag PF=ParamFlag::Default>
struct InOut : public In<T,PF>
{
typedef In<T,PF> Base;
enum { is_output = true, need_input = !(PF & ParamFlag::NoInput) };
InOut(const js::Value& v, Context* ctx, unsigned param_nr)
: Base(v, ctx, param_nr)
{ }
js::Value to_json() const
{
return ::to_json<T>(Base::value);
}
};
template<class T, ParamFlag PF = ParamFlag::Default>
struct Out
{
typedef T* c_type; // the according type in C function parameter
enum { is_output = true, need_input = !(PF & ParamFlag::NoInput) }; // if need_input=false it would no longer consume an element in the input parameter array.
explicit Out() : value{}
{ }
explicit Out(const T& t) : value{t}
{ }
~Out();
Out(const Out<T,PF>& other) = delete;
Out(Out<T,PF>&& victim) = delete;
// just to be sure they are not implicitly defined:
Out<T,PF>& operator=(const Out<T,PF>& other) = delete;
Out<T,PF>& operator=(Out<T,PF>&& victim) = delete;
// dummy Value v is ignored for output parameters
Out(const js::Value& v, Context*, unsigned param_nr)
: Out()
{ }
js::Value to_json() const
{
return ::to_json<T>(value);
}
c_type get_value() const { return &value; }
mutable T value;
/*
friend
std::ostream& operator<<(std::ostream& o, const Out<T,PF>& out)
{
o << (const void*)out;
o << ", value=" << (const void*)out.value;
return o;
}
*/
};
// helper classes to specify in- and out-parameters whose output might change by the called function.
// Use InOut<T> for in/out parameters where the function only makes in-place changes.
template<class T, ParamFlag PF=ParamFlag::Default>
struct InOutP : public Out<T,PF>
{
typedef Out<T,PF> Base;
enum { is_output = true, need_input = !(PF & ParamFlag::NoInput) };
explicit InOutP(const T& t) : Base(t) {}
InOutP<T,PF>& operator=(const InOutP<T,PF>&) = delete;
// default implementation:
InOutP(const js::Value& v, Context*, unsigned param_nr)
: Base( from_json<T>(v) )
{ }
};
template<class T, ParamFlag PF>
js::Value to_json(const Out<T,PF>& o)
{
return ::to_json(o.value);
}
template<class T, ParamFlag PF>
js::Value to_json(const InOut<T,PF>& o)
{
return ::to_json(o.value);
}
// Type2String
template<class T>
struct Type2String
{
static js::Value get();
};
template<class T, ParamFlag PF>
struct Type2String<In<T, PF>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "In"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
template<class T>
struct Type2String<In<T, ParamFlag::NoInput>>
{
static js::Value get() { throw std::logic_error("Stupid MSVC compiler requiring this!"); }
};
template<class T>
struct Type2String<InRaw<T, ParamFlag::Default>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "In"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
template<class T, ParamFlag PF>
struct Type2String<Out<T, PF>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "Out"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
template<class T, ParamFlag PF>
struct Type2String<InOut<T, PF>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "InOut"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
template<class T, ParamFlag PF>
struct Type2String<InOutP<T, PF>>
{
static js::Value get() { js::Object ret; ret.emplace_back("direction", "InOut"); ret.emplace_back("type", Type2String<T>::get() ); return ret; }
};
template<class... Args> struct Type2Json;
template<class T, class... Args>
struct Type2Json<T, Args...>
{
static js::Array& get(js::Array& a)
{
if(T::need_input)
{
a.push_back( Type2String<T>::get() );
}
Type2Json<Args...>::get(a);
return a;
}
};
template<> struct Type2Json<>
{
static js::Array& get(js::Array& a) { return a; }
};
#endif // PEP_JSON_SERVER_INOUT_HH