Advertisement
Rochet2

Unpack tuple as parameters c++14

Jan 5th, 2018
288
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.41 KB | None | 0 0
  1. // ConsoleApplication1.cpp : Defines the entry point for the console application.
  2. //
  3.  
  4. #include "stdafx.h"
  5.  
  6. #include <iostream>
  7. #include <memory>
  8. #include <unordered_map>
  9. #include <vector>
  10. #include <utility>
  11. #include <tuple>
  12. #include <string>
  13. #include <type_traits>
  14.  
  15. #include <memory>
  16.  
  17. /*a replacement for std::optional for default values that may or may not exist*/
  18. template<typename T>
  19. struct Optional
  20. {
  21.     Optional() { }
  22.     Optional(T const& v) : v(std::make_unique<T>(v)) { }
  23.     Optional(T&& v) : v(std::make_unique<T>(std::forward<T>(v))) { }
  24.     operator bool() const { return v.operator bool(); }
  25.     std::unique_ptr<T> v;
  26. };
  27.  
  28. /*dummy player class for testing, contains various functions to test binding with*/
  29. struct Player {
  30.     void SetName(std::string const & name) {
  31.         std::cout << "Called " << name << std::endl;
  32.     }
  33.     std::string SetName2(std::string const & name) {
  34.         return name;
  35.     }
  36.     void overloaded(int i) {
  37.         std::cout << "Called " << i << std::endl;
  38.     }
  39.     void overloaded(double d) {
  40.         std::cout << "Called " << d << std::endl;
  41.     }
  42.     void multiple_parameters(int a, double b, std::string c, const char* d) {
  43.         std::cout << "Called " << a << b << c << d << std::endl;
  44.     }
  45.     void alltypes(int a, std::string b, const char* c, void* d, std::string const & e) {
  46.         std::cout << "Called " << a << b << c << d << e << std::endl;
  47.     }
  48.     std::string& refreturn() {
  49.         static std::string ref = "ref";
  50.         return ref;
  51.     }
  52.     std::string* ptrreturn() {
  53.         static std::string ref = "ptr";
  54.         return &ref;
  55.     }
  56. };
  57.  
  58. /*this class represents the lua stack interface which allows pushing and reading(checking) values from the stack*/
  59. struct State {
  60.     State(void*) {}
  61.     template<typename T> void Push(T v) { std::cout << "Pushed: " << v << std::endl; };
  62.     template<typename T> T CHECKVAL(int i, T def) { return def; };
  63.     template<typename T> T CHECKVAL(int i) { return T(); };
  64. };
  65.  
  66. /*a base function type, this will be used to store functions in some place for simple access to make function calls.
  67. Different implementations of binding functions can inherit this and override how the function is called*/
  68. class fbase
  69. {
  70. public:
  71.     fbase(std::string const& name) : name(name) {};
  72.     virtual ~fbase() = default;
  73.     virtual int call(State& state) = 0;
  74.     std::string name;
  75. };
  76.  
  77. /*This is a specialized class to store function signatures and call the functions with values from lua stack later on*/
  78. template<
  79.     typename F, /*real function pointer signature*/
  80.     typename R, /*type used for pushing return value to stack. Can be void to not push anything even if there is a return value*/
  81.     typename C, /*type used the check the object itself from stack (type of self)*/
  82.     typename... P /*types used for checking values from stack to be used as function parameters*/
  83. >
  84. class func : public fbase
  85. {
  86. public:
  87.     typedef std::tuple<Optional<P>...> Defaults;
  88.     typedef std::tuple<P...> Arguments;
  89.     static constexpr auto size = std::tuple_size<Arguments>::value;
  90.     static constexpr auto seq = std::make_index_sequence<size>{};
  91.  
  92.     func(std::string const& name, F f) : fbase(name), f(f), d() { }
  93.     template<typename ...T, typename = std::enable_if<(size > 0)>>
  94.     func(std::string const& name, F f, T&&... args) : fbase(name), f(f), d(std::forward<T>(args)...) { }
  95.  
  96.     /*override the base method that should call a function.*/
  97.     int call(State& state) override
  98.     {
  99.         C sel = state.CHECKVAL<C>(0); // self is at idx 0
  100.         Arguments args;
  101.         fill_tuple(state, args, seq); // check parameters from stack
  102.         return call_function(state, sel, args, seq); // call function with self and the parameters
  103.     }
  104.  
  105.     /*allows setting a default value for some specific parameter index, for example if function is (int,double), you can do func(...).def<1>(1.5)*/
  106.     template <size_t I, typename T>
  107.     auto& def(T&& t)
  108.     {
  109.         std::get<I>(d) = typename std::tuple_element<I, Defaults>::type(std::forward<T>(t));
  110.         return *this;
  111.     }
  112.  
  113. private:
  114.     /*helper function to fill to tuple a single value checked from stack at index I */
  115.     template <size_t I>
  116.     void fill_tuple_helper(State& state, Arguments& args)
  117.     {
  118.         auto& v = std::get<I>(args);
  119.         auto const & def = std::get<I>(d);
  120.         // self is at idx 0, so parameters start at 1, which means we need to use I+1
  121.         if (def)
  122.             v = state.CHECKVAL<typename std::tuple_element<I, Arguments>::type>(I + 1, *def.v);
  123.         else
  124.             v = state.CHECKVAL<typename std::tuple_element<I, Arguments>::type>(I + 1);
  125.     }
  126.  
  127.     /*fill tuple by checking values at given template argument indexes*/
  128.     template <size_t ... I>
  129.     void fill_tuple(State& state, Arguments& args, const std::index_sequence<I ...>&)
  130.     {
  131.         // Calling a function for each type in a tuple from https://stackoverflow.com/questions/16387354/template-tuple-calling-a-function-on-each-element
  132.         auto l = { 0, (fill_tuple_helper<I>(state, args), 0)... };
  133.     }
  134.  
  135.     /*call the function with the tuple of parameters. Since return type is defined as void, dont push anything after calling the function*/
  136.     template<size_t ... I, typename T = R>
  137.     typename std::enable_if<std::is_void<T>::value, int>::type
  138.         call_function(State&, C& o, Arguments& args, const std::index_sequence<I ...>&)
  139.     {
  140.         // Arguments parameter unpacking from https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer
  141.         (o->*f)(std::get<I>(args) ...);
  142.         return 0;
  143.     }
  144.  
  145.     /*call the function with the tuple of parameters. Since there is a return value, we push it to stack after calling the function*/
  146.     template<size_t ... I, typename T = R>
  147.     typename std::enable_if<!std::is_void<T>::value, int>::type
  148.         call_function(State& state, C& o, Arguments& args, const std::index_sequence<I ...>&)
  149.     {
  150.         state.Push<R>((o->*f)(std::get<I>(args) ...));
  151.         return 1;
  152.     }
  153.  
  154.     F f;
  155.     Defaults d;
  156. };
  157.  
  158. int main() {
  159.     State s(nullptr);
  160.     func<void (Player::*)(std::string const &), void, Player*, std::string>("SetName", &Player::SetName).def<0>("teasdstd").call(s);
  161.     func<decltype(&Player::SetName), void, Player*, std::string>("SetName", &Player::SetName).def<0>("teasdst").call(s);
  162.     func<decltype(&Player::SetName2), std::string, Player*, std::string>("SetName2", &Player::SetName2).call(s);
  163.     func<decltype((void (Player::*)(int))&Player::overloaded), void, Player*, int>("overloaded", &Player::overloaded).def<0>(5).call(s);
  164.     func<decltype(&Player::multiple_parameters), void, Player*, int, double, std::string, const char*>("multiple_parameters", &Player::multiple_parameters).def<1>(1.5).def<2>("asd").def<3>("cstr").call(s);
  165.     func<decltype(&Player::alltypes), void, Player*, int, std::string, const char*, void*, std::string>("alltypes", &Player::alltypes).def<2>("cstr").def<3>(nullptr).def<4>("strref").call(s);
  166.     func<decltype(&Player::alltypes), void, Player*, int, std::string, const char*, void*, std::string>("alltypes", &Player::alltypes, 1, std::string("asd"), "bsd", nullptr, std::string("asd")).call(s);
  167.     func<decltype(&Player::refreturn), std::string, Player*>("refreturn", &Player::refreturn).call(s);
  168.     func<decltype(&Player::ptrreturn), std::string*, Player*>("ptrreturn", &Player::ptrreturn).call(s);
  169.     return 0;
  170. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement