fork(1) download
  1. #include <iostream>
  2. #include <type_traits>
  3.  
  4. namespace ParameterCheck {
  5. namespace ParamGetter {
  6. // Based on an answer from GManNickG ( http://stackoverflow.com/a/4693493/5386374 )
  7.  
  8. // Turn the type list into a single type we can use with std::is_same.
  9. template<typename... Ts> struct variadic_typedef { };
  10.  
  11. // Generic case, to catch passed parameter types list.
  12. template<typename... Ts> struct variadic_wrapper {
  13. using type = variadic_typedef<Ts...>;
  14. };
  15.  
  16. // Special case to catch void parameter types list.
  17. template<> struct variadic_wrapper<> {
  18. using type = variadic_typedef<void>;
  19. };
  20.  
  21. // Generic case to isolate parameter list from function signature.
  22. template<typename RT, typename... Ts> struct variadic_wrapper<RT (*)(Ts...)> {
  23. using type = variadic_typedef<Ts...>;
  24. };
  25.  
  26. // Special case to isolate void parameter from function signature.
  27. template<typename RT> struct variadic_wrapper<RT (*)()> {
  28. using type = variadic_typedef<void>;
  29. };
  30.  
  31. /*
  32.   template<typename... Ts> struct convert_in_tuple {
  33.   using type = std::tuple<Ts...>;
  34.   };
  35.  
  36.   template<typename... Ts> struct convert_in_tuple<variadic_typedef<Ts...> > {
  37.   using type = typename convert_in_tuple<Ts...>::type;
  38.   };
  39. */
  40. } // namespace ParamGetter
  41.  
  42. template<typename... Ts> using PGetter = typename ParamGetter::variadic_wrapper<Ts...>::type;
  43.  
  44. // template<typename... Ts> using PGetter = typename ParamGetter::convert_in_tuple<ParamGetter::variadic_typedef<Ts...> >::type;
  45.  
  46. // Declare class template.
  47. template<typename... Ts> struct parameter_match;
  48.  
  49. // Actual class. Becomes either std::true_type or std::false_type.
  50. template<typename F, typename... Ts> struct parameter_match<F, Ts...> : public std::integral_constant<bool, std::is_same<PGetter<F>, PGetter<Ts...> >{}> {};
  51. } // namespace ParameterCheck
  52.  
  53. template<typename F, typename... Ts> using PChecker = ParameterCheck::parameter_match<F, Ts...>;
  54. // template<typename F, typename... Ts> using PChecker = ParameterCheck::parameter_match<F, Ts&...>;
  55.  
  56. // -------------------------
  57.  
  58. // Looking for a way to just grab the parameter list right out of the passed function, feel free to ignore this section for now.
  59. namespace ParameterListCapture {
  60. template<typename T> struct p_list;
  61.  
  62. template<typename RT> struct p_list<RT (*)()> { };
  63.  
  64. /*
  65. // Doesn't work, sadly.
  66. template<typename RT, typename... Ts> struct p_list<RT (*)(Ts...)> {
  67. using type = Ts...;
  68. };
  69. */
  70.  
  71. } // namespace ParameterListCapture
  72.  
  73. // -------------------------
  74.  
  75. // The actual calling function.
  76. template<typename Func, typename... Ts> auto caller2(std::true_type x, Func f, Ts... args) {
  77. std::cout << "Now calling... ";
  78. return f(args...);
  79. }
  80.  
  81. // Parameter mismatch overload.
  82. template<typename Func, typename... Ts> auto caller2(std::false_type x, Func f, Ts... args) {
  83. std::cout << "Parameter list mismatch." << std::endl;
  84. }
  85.  
  86. // Wrapper to check for parameter mismatch.
  87. template<typename Func, typename... Ts> auto caller(Func f, Ts... args) {
  88. // return caller2(ParameterCheck::parameter_match<Func, Ts...>{}, f, args...);
  89. return caller2(PChecker<Func, Ts...>{}, f, args...);
  90. }
  91.  
  92. // -------------------------
  93.  
  94. void f1() { std::cout << "Hi." << std::endl; }
  95. int f2(int a, int b, int* c) {
  96. std::cout << a + b + *c << std::endl;
  97. return b - a;
  98. }
  99. void f3(const int a, const bool b, bool c) { std::cout << "Arguments? What arguments?" << std::endl; }
  100. bool f4(int& a, const float b) { std::cout << (b > a); return (a > b); }
  101. void f5(const char* a) { std::cout << a << std::endl; }
  102.  
  103. void f0(int& a) {
  104. std::cout << "f5(): Value is: " << a++ << std::endl;
  105. std::cout << "Incremented, returning." << std::endl;
  106. }
  107.  
  108. void f01(int a) { std::cout << "Let's see what this does." << std::endl; }
  109.  
  110.  
  111. template<typename Func, typename... Ts> void checker(Func f, Ts... args) {
  112. std::cout << PChecker<Func, Ts...>{} << std::endl ;
  113. caller(f, args...);
  114. }
  115.  
  116. template<typename Func, typename T> void passThrough2(Func f, T t) {
  117. // template<typename Func, typename T> void passThrough2(Func f, T& t) { // Makes f0() work properly, but still not pass checker().
  118. f(t);
  119. std::cout << "passThrough2(): Value is now: " << t << std::endl;
  120. }
  121.  
  122. template<typename Func, typename... Ts> void passThrough(Func f, Ts... args) {
  123. // template<typename Func, typename... Ts> void passThrough(Func f, Ts&... args) { // Makes f0() work properly, but still not pass checker().
  124. std::cout << "Passing through passThrough()..." << std::endl;
  125. passThrough2(f, args...);
  126. }
  127.  
  128. int main() {
  129. int a = -17, x = 3;
  130. int refA = a;
  131. const float b = 1.2f;
  132. bool c = false;
  133. const char* d = "Hihihi.\n";
  134. char* e = const_cast<char*>(d);
  135. std::cout << std::boolalpha;
  136.  
  137. std::cout << "Empty parameter list testing:" << std::endl;
  138. std::cout << "Testing (void(*)(), void): ";
  139. checker(f1);
  140.  
  141. std::cout << "Testing (void(*)(), int): ";
  142. checker(f1, 3);
  143.  
  144. std::cout << "Testing (void(*)(), void*): ";
  145. checker(f1, (void*) nullptr);
  146.  
  147. std::cout << " - - - -" << std::endl;
  148. std::cout << "Standard parameters (including pointers) testing:" << std::endl;
  149. std::cout << "Testing (int(*)(int, int, int*), int, int, int*): ";
  150. checker(f2, 3, 9, &a);
  151.  
  152. std::cout << "Testing (int(*)(int, int, int*), int*, int, int): ";
  153. checker(f2, &a, 3, 9);
  154.  
  155. std::cout << " - - - -" << std::endl;
  156. std::cout << "Const testing:" << std::endl;
  157. std::cout << "Testing (void(*)(const int, const bool, bool), int, bool [literal], bool [literal]): ";
  158. checker(f3, a, true, false);
  159.  
  160. std::cout << "Testing (void(*)(const int, const bool, bool), int [literal], bool, bool): ";
  161. checker(f3, 4, c, c);
  162.  
  163. std::cout << "Testing (void(*)(const char*), const char*): ";
  164. checker(f5, d);
  165.  
  166. std::cout << "Testing (void(*)(const char*), char*): ";
  167. checker(f5, e);
  168. std::cout << "Adds const-ness to primitives during function call, but not to pointers." << std::endl;
  169.  
  170. std::cout << " - - - -" << std::endl;
  171. std::cout << "Reference testing:" << std::endl;
  172. std::cout << "Testing (bool(*)(int&, const float), int&, const float): ";
  173. checker(f4, a, b);
  174.  
  175. std::cout << "Testing (bool(*)(int&, const float), [explicit] int&, float): ";
  176. checker(f4, refA, b);
  177.  
  178. std::cout << "Testing (bool(*)(int&, float), int&, int): ";
  179. checker(f4, a, a);
  180. std::cout << "Still some issues." << std::endl;
  181.  
  182. std::cout << " - - - -" << std::endl;
  183. std::cout << "Testing passing reference through variadic template:" << std::endl;
  184. std::cout << "Variable 'x' original value: " << x << std::endl;
  185. passThrough(f0, x);
  186. std::cout << "And finally, back in main(): 'x' new value: " << x << std::endl;
  187.  
  188. std::cout << "Let's try it straight into passThrough2(): " << std::endl;
  189. std::cout << "x starting value: " << x << std::endl;
  190. passThrough2(f0, x);
  191. std::cout << "x new value: " << x << std::endl;
  192.  
  193. std::cout << "And now, one last thing, 1) passing (int) to template func expecting (int&):" << std::endl;
  194. passThrough(f01, x);
  195.  
  196. std::cout << "And 2) Checking function signatures:" << std::endl;
  197. std::cout << "Comment out the above checker() calls, and change \"Ts...\" to \"Ts&...\" in checker()." << std::endl;
  198. std::cout << "Also, use the second version of PChecker (currently commented out), instead of the first." << std::endl;
  199. std::cout << "Note that if we explicitly specify that a parameter [pack] is a reference(s) like this, PChecker matching to non-reference types will fail." << std::endl;
  200. std::cout << "Testing for (void(*)(int&), int&): ";
  201. checker(f0, x);
  202. std::cout << "Testing for (void(*)(int), int&): ";
  203. checker(f1, x);
  204.  
  205.  
  206. }
Success #stdin #stdout 0s 3464KB
stdin
Standard input is empty
stdout
Empty parameter list testing:
Testing (void(*)(), void): true
Now calling... Hi.
Testing (void(*)(), int): false
Parameter list mismatch.
Testing (void(*)(), void*): false
Parameter list mismatch.
 - - - -
Standard parameters (including pointers) testing:
Testing (int(*)(int, int, int*), int, int, int*): true
Now calling... -5
Testing (int(*)(int, int, int*), int*, int, int): false
Parameter list mismatch.
 - - - -
Const testing:
Testing (void(*)(const int, const bool, bool), int, bool [literal], bool [literal]): true
Now calling... Arguments?  What arguments?
Testing (void(*)(const int, const bool, bool), int [literal], bool, bool): true
Now calling... Arguments?  What arguments?
Testing (void(*)(const char*), const char*): true
Now calling... Hihihi.

Testing (void(*)(const char*), char*): false
Parameter list mismatch.
Adds const-ness to primitives during function call, but not to pointers.
 - - - -
Reference testing:
Testing (bool(*)(int&, const float), int&, const float): false
Parameter list mismatch.
Testing (bool(*)(int&, const float), [explicit] int&, float): false
Parameter list mismatch.
Testing (bool(*)(int&, float), int&, int): false
Parameter list mismatch.
Still some issues.
 - - - -
Testing passing reference through variadic template:
Variable 'x' original value: 3
Passing through passThrough()...
f5(): Value is: 3
Incremented, returning.
passThrough2(): Value is now: 4
And finally, back in main(): 'x' new value: 3
Let's try it straight into passThrough2(): 
x starting value: 3
f5(): Value is: 3
Incremented, returning.
passThrough2(): Value is now: 4
x new value: 3
And now, one last thing, 1) passing (int) to template func expecting (int&):
Passing through passThrough()...
Let's see what this does.
passThrough2(): Value is now: 3
And 2) Checking function signatures:
Comment out the above checker() calls, and change "Ts..." to "Ts&..." in checker().
Also, use the second version of PChecker (currently commented out), instead of the first.
Note that if we explicitly specify that a parameter [pack] is a reference(s) like this, PChecker matching to non-reference types will fail.
Testing for (void(*)(int&), int&): false
Parameter list mismatch.
Testing for (void(*)(int), int&): false
Parameter list mismatch.