p≡p JSON adapter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
8.2 KiB

5 years ago
5 years ago
5 years ago
  1. #ifndef FUNCTION_MAP_HH
  2. #define FUNCTION_MAP_HH
  3. #include "inout.hh"
  4. #include "json_spirit/json_spirit_value.h"
  5. #include "json_spirit/json_spirit_writer.h"
  6. #include "context.hh"
  7. #include "logger.hh"
  8. #include <type_traits>
  9. // Just for debugging:
  10. #include <iostream>
  11. #include <pEp/message_api.h>
  12. template<class R>
  13. struct Return
  14. {
  15. typedef R return_type;
  16. typedef Out<R> out_type;
  17. };
  18. template<class R, ParamFlag PF>
  19. struct Return< Out<R, PF> >
  20. {
  21. typedef R return_type;
  22. typedef Out<R, PF> out_type;
  23. };
  24. // heloer class for generic calls:
  25. // R : return type of the called function
  26. // U : number of the parameter which is being extracted
  27. // MAX: maximum number of parameters. if U==MAX the function call is executed
  28. // Args... the function's parameter types
  29. template<class R, unsigned U, unsigned MAX, class... Args>
  30. class helper;
  31. // specialization for U==MAX: do the function call here
  32. template<class R, unsigned U, class... Args>
  33. class helper<R, U, U, Args...>
  34. {
  35. public:
  36. typedef typename Return<R>::return_type ReturnType;
  37. enum { nr_of_output_params = 0 };
  38. enum { nr_of_input_params = 0 };
  39. static void copyParam( js::Array& dest, const js::Array& src, unsigned index )
  40. {
  41. // do nothing. :-)
  42. }
  43. static js::Value call( const std::function< ReturnType(typename Args::c_type...)>& fn, Context*, js::Array& out_parameters, const js::Array& parameters, const Args&... args)
  44. {
  45. typename Return<R>::out_type o{ fn(args.get_value()...) };
  46. return to_json( o );
  47. }
  48. };
  49. // specialization for Return type == void
  50. template<unsigned U, class... Args>
  51. class helper<void, U, U, Args...>
  52. {
  53. public:
  54. enum { nr_of_output_params = 0 };
  55. enum { nr_of_input_params = 0 };
  56. static void copyParam( js::Array& dest, const js::Array& src, unsigned index )
  57. {
  58. // do nothing. :-)
  59. }
  60. static js::Value call( const std::function<void(typename Args::c_type...)>& fn, Context*, js::Array& out_parameters, const js::Array& parameters, const Args&... args)
  61. {
  62. fn(args.get_value()...);
  63. return js::Value{};
  64. }
  65. };
  66. // recursive helper class:
  67. // It is used with U==0 in Func<>::call() and calls itself recursively until U==MAX, where the real function calls occurs,
  68. // and the output parameters are collected during unwinding of the recursion
  69. template<class R, unsigned U, unsigned MAX, class... Args>
  70. class helper
  71. {
  72. public:
  73. typedef typename Return<R>::return_type ReturnType;
  74. typedef std::tuple<Args...> Tuple;
  75. typedef typename std::tuple_element<U, Tuple>::type Element; // The type of the U'th parameter
  76. typedef helper<R, U+1, MAX, Args...> NextHelper;
  77. enum { nr_of_output_params = int(Element::is_output) + NextHelper::nr_of_output_params };
  78. enum { nr_of_input_params = int(Element::need_input) + NextHelper::nr_of_input_params };
  79. static void copyParam( js::Array& dest, const js::Array& src, unsigned index )
  80. {
  81. if(Element::need_input)
  82. {
  83. dest.push_back( src.at(index) );
  84. ++index;
  85. }else{
  86. dest.push_back( js::Value{} ); // insert dummy parameter
  87. }
  88. NextHelper::copyParam( dest, src, index );
  89. }
  90. // A2... a2 are the alredy pealed-off paremeters
  91. template<class... A2>
  92. static js::Value call( const std::function< ReturnType(typename Args::c_type...)>& fn, Context* ctx, js::Array& out_parameters, const js::Array& parameters, const A2&... a2)
  93. {
  94. // extract the U'th element of the parameter list
  95. const Element element(parameters[U], ctx, U);
  96. const js::Value ret = NextHelper::call(fn, ctx, out_parameters, parameters, a2..., element );
  97. if(Element::is_output)
  98. {
  99. js::Value out = element.to_json();
  100. // std::cerr << "|$ Out #" << U << " : " << js::write(out) << "\n";
  101. out_parameters.push_back( std::move(out) );
  102. }else{
  103. // std::cerr << "|$ Param #" << U << " is not for output.\n";
  104. }
  105. return ret;
  106. }
  107. };
  108. // abstract base class for all Func<...> types below
  109. class FuncBase
  110. {
  111. public:
  112. virtual ~FuncBase() = default;
  113. virtual bool isSeparator() const = 0;
  114. virtual void setJavaScriptSignature(js::Object& o) const = 0;
  115. virtual js::Value call(const js::Array& params, Context* context) const = 0;
  116. };
  117. template<class R, class... Args>
  118. class Func : public FuncBase
  119. {
  120. public:
  121. typedef typename Return<R>::return_type ReturnType;
  122. virtual ~Func() = default;
  123. virtual bool isSeparator() const noexcept override { return false; }
  124. explicit Func( const std::function<ReturnType(typename Args::c_type ...)>& _f )
  125. : fn(_f)
  126. {}
  127. Func(const Func<R, Args...>&) = delete;
  128. void operator=(const Func<R, Args...>&) = delete;
  129. std::function<ReturnType(typename Args::c_type ...)> fn;
  130. js::Value call(const js::Array& parameters, Context* context) const override
  131. {
  132. typedef helper<R, 0, sizeof...(Args), Args...> Helper;
  133. if(parameters.size() != Helper::nr_of_input_params)
  134. throw std::runtime_error("Size mismatch: "
  135. "Array has " + std::to_string( parameters.size() ) + " element(s), "
  136. "but I expect " + std::to_string( Helper::nr_of_input_params) + " element(s)! "
  137. );
  138. const js::Array* p_params = &parameters;
  139. // create a copy of the parameters only if necessary
  140. js::Array param_copy;
  141. if( Helper::nr_of_input_params != sizeof...(Args) )
  142. {
  143. param_copy.reserve( Helper::nr_of_input_params );
  144. Helper::copyParam( param_copy, parameters, 0u );
  145. p_params = &param_copy; // use the copy instead of 'parameters'
  146. }
  147. // recursive template magic breaks loose:
  148. // recursively extract the JSON parameters, call 'fn' and collect its return value
  149. // and all output parameters into a tuple<> and return it as JSON array
  150. js::Array out_params;
  151. out_params.reserve( Helper::nr_of_output_params );
  152. js::Value ret = Helper::call(fn, context, out_params, *p_params);
  153. js::Object rs;
  154. rs.emplace_back("outParams", std::move(out_params));
  155. rs.emplace_back("return", std::move(ret));
  156. context->augment(rs); // used e.g. add some debug infos to the status return value
  157. context->clear(); // clear all stored values, if any.
  158. return rs;
  159. }
  160. void setJavaScriptSignature(js::Object& o) const override
  161. {
  162. js::Array params;
  163. Type2Json<Args...>::get(params);
  164. o.emplace_back( "return", Type2String<R>::get() );
  165. o.emplace_back( "params", std::move(params) );
  166. o.emplace_back( "separator", false );
  167. }
  168. };
  169. template<class R, class... Args>
  170. class FuncCache : public Func<R, Args...>
  171. {
  172. public:
  173. typedef Func<R, Args...> Base;
  174. typedef typename Return<R>::return_type ReturnType;
  175. typedef helper<R, 0, sizeof...(Args), Args...> Helper;
  176. FuncCache(const std::string& _func_name, const std::function<ReturnType(typename Args::c_type ...)>& _f )
  177. : Base(_f)
  178. , func_name(_func_name)
  179. {}
  180. js::Value call(const js::Array& parameters, Context* context) const override
  181. {
  182. Logger Log("FuncCache::call");
  183. typedef std::tuple<typename Args::c_type...> param_tuple_t;
  184. param_tuple_t param_tuple;
  185. // FIXME: Does only work with functions with type: void(PEP_SESSION, T):
  186. const auto p0 = from_json< typename std::tuple_element<1, param_tuple_t>::type >(parameters[0]);
  187. Log << Logger::Debug << "func_name=\"" << func_name << "\", value=" << p0 << ".";
  188. std::function<void(PEP_SESSION)> func = std::bind(Base::fn, std::placeholders::_1, p0);
  189. context->cache(func_name, func);
  190. return Base::call(parameters, context);
  191. }
  192. private:
  193. const std::string func_name;
  194. };
  195. // Just a separating placeholder in the drop-down list. Does not calls anything.
  196. class Separator : public FuncBase
  197. {
  198. public:
  199. Separator() = default;
  200. virtual bool isSeparator() const noexcept override { return true; }
  201. virtual void setJavaScriptSignature(js::Object& o) const override { o.emplace_back("separator", true); }
  202. virtual js::Value call(const js::Array&, Context*) const override { return js::Value{}; }
  203. };
  204. //typedef std::map< std::string, FuncBase* > FunctionMap;
  205. typedef std::vector< std::pair< std::string, FuncBase*> > FunctionMapBase;
  206. class FunctionMap
  207. {
  208. public:
  209. typedef FunctionMapBase::value_type value_type;
  210. typedef FunctionMapBase::const_iterator const_iterator;
  211. const_iterator begin() const noexcept { return v.begin(); }
  212. const_iterator end() const noexcept { return v.end(); }
  213. const_iterator find(const std::string&) const noexcept;
  214. FunctionMap(std::initializer_list<value_type> il);
  215. ~FunctionMap();
  216. private:
  217. FunctionMapBase v;
  218. };
  219. typedef FunctionMap::value_type FP;
  220. #endif // FUNCTION_MAP_HH