#include <iostream>
#include <type_traits>
#include <functional>
#include <cstdint>
#include <typeinfo>
#include <string>
// Just gonna do this for the sake of our example here.
using FilePath = std::string;
// Parameter match checking.
namespace ParameterCheck {
template<typename T, typename... Ts> struct parameter_match : public std::false_type {};
// Declare (GetColor, int16_t*) valid.
template<> struct parameter_match<int (*)(int16_t*), int16_t*> : public std::true_type {};
// Declare (GetFile, FilePath&) valid.
// template<> struct parameter_match<int (*)(FilePath&), FilePath&> : public std::true_type {}; // You'd think this would work, but...
template<> struct parameter_match<int (*)(FilePath&), FilePath> : public std::true_type {}; // Nope!
// For some reason, reference-ness isn't part of the templated type. It acts as if it was "template<typename T> void func(T& arg)" instead.
// Declare (WriteDocument, const FilePath&, const char*, bool) valid.
// template<> struct parameter_match<int (*)(const FilePath&, const char*, bool), const FilePath, const char*, bool> : public std::true_type {};
// template<> struct parameter_match<int (*)(const FilePath&, const char*, bool), const FilePath&, const char*, bool> : public std::true_type {};
template<> struct parameter_match<int (*)(const FilePath&, const char*, bool), FilePath, const char*, bool> : public std::true_type {};
// More reference-as-template-parameter wonkiness: Out of these three, only the last works.
// Declare everything without a parameter list valid.
template<typename T> struct parameter_match<T (*)()> : public std::true_type { };
} // namespace ParameterCheck
// Discount return type deduction:
namespace ReturnTypeCapture {
// Credit goes to Angew ( http://stackoverflow.com/a/18695701/5386374 )
template<typename T> struct ret_type;
template<typename RT, typename... Ts> struct ret_type<RT (*)(Ts...)> {
using type = RT;
};
} // namespace ReturnTypeCapture
// Alias declarations:
template<typename F, typename... Ts> using PChecker = ParameterCheck::parameter_match<F, Ts...>;
template <typename F> using RChecker = typename ReturnTypeCapture::ret_type<F>::type;
// ---------------
// Quick implementations of your example functions.
int GetColor(int16_t* color) {
std::cout << "GetColor(int16_t*) with parameter: " << *color << std::endl;
return 3;
}
int GetFile(FilePath& file) {
std::cout << "GetFile(FilePath&)." << std::endl;
std::cout << "..." << file << "? Get it yourself!" << std::endl;
return -6;
}
int WriteDocument(const FilePath& file, const char* fileFormatName, bool askForParams) {
std::cout << "WriteDocument(const FilePath&, const char*, bool) with: "
<< file << ", " << fileFormatName << ", and " << std::boolalpha
<< askForParams << "." << std::noboolalpha << std::endl;
std::cout << "Eh, I'll write it tomorrow." << std::endl;
return 8;
}
// ---------------
// The actual calling function, C++11 style.
template<typename Func, typename... Ts> auto caller2(std::true_type x, Func f, Ts... args) -> RChecker<Func> {
std::cout << "Now calling... ";
return f(args...);
}
// Parameter mismatch overload.
template<typename Func, typename... Ts> auto caller2(std::false_type x, Func f, Ts... args) -> RChecker<Func> {
std::cout << "Parameter list mismatch." << std::endl;
return static_cast<RChecker<Func> >(0); // Just to make sure we don't break stuff.
}
// Wrapper to check for parameter mismatch, C++14 style.
template<typename Func, typename... Ts> auto caller(Func f, Ts... args) /* -> RChecker<Func> */ {
// return caller2(ParameterCheck::parameter_match<Func, Ts...>{}, f, args...);
return caller2(PChecker<Func, Ts...>{}, f, args...);
}
// ---------------
// To show what std::true_type and std::false_type act as.
template<bool N> void fnarg2(std::integral_constant<bool, N> x) {
std::cout << "Integral constant<bool, N> value: " << x.value << std::endl;
}
template<typename F, typename... Ts> void fnarg(F f, Ts... ts) {
fnarg2(ParameterCheck::parameter_match<F, Ts...>{});
}
// ---------------
// Something I used to get things working, I left it here to show something odd:
// Which one is true and which is false flips depending on whether the parameter is T or T&.
template<typename T> void sameness(T& t) {
std::cout << std::boolalpha;
std::cout << "cfp is const: " << std::is_same<T, const FilePath>::value << std::endl;
std::cout << "cfp ain't const: " << std::is_same<T, FilePath>::value << std::endl;
std::cout << std::noboolalpha;
}
// ---------------
// Return type sorcery:
std::string f1() { return std::string("Nyahaha."); }
int f2() { return -42; }
char f3() { return '&'; }
// template<typename R, typename F> auto rtCaller2(R r, F f) -> typename R::type {
// template<typename F> auto rtCaller2(F f) -> typename ReturnTypeCapture::ret_type<F>::type {
template<typename F> auto rtCaller2(F f) -> RChecker<F> {
return f();
}
template<typename F> void rtCaller(F f) {
// auto a = rtCaller2(ReturnTypeCapture::ret_type<F>{}, f);
auto a = rtCaller2(f);
std::cout << a << " (type: " << typeid(a).name() << ")" << std::endl;
}
// ---------------
int main() {
int16_t i = 3333;
FilePath fp = "C:\\Windows\\win.ini";
const FilePath cfp = (const FilePath) fp; // In case you didn't know, it's a const FilePath. ^_^
const char* cc = "Fred Format\n";
bool b = true;
// Checking int(*)(int16_t*):
auto gc = caller(GetColor, &i); // Valid call.
caller(GetColor, &i, i); // Invalid call.
std::cout << std::endl;
// Checking int(*)(FilePath&):
auto gf = caller(GetFile, fp); // Valid call.
caller(GetFile, main); // Invalid call.
std::cout << std::endl;
//Checking int(*)(const FilePath&, const char*, bool):
auto wd = caller(WriteDocument, cfp, cc, b); // Valid call.
caller(WriteDocument, cfp, const_cast<char*>(cc), b); // Invalid call.
std::cout << std::endl;
auto f_1 = caller(f1);
auto f_2 = caller(f2);
auto f_3 = caller(f3);
std::cout << std::endl;
std::cout << "Return values: " << gc << ", " << gf << ", " << wd << ", "
<< f_1 << ", " << f_2 << ", and " << f_3 << std::endl;
// std::cout << std::endl;
// sameness(cfp);
// Three std::true_types, and one std::false_type.
std::cout << std::boolalpha << std::endl;
fnarg(GetColor, &i);
fnarg(GetFile, fp);
fnarg(WriteDocument, cfp, cc, b);
fnarg(i);
std::cout << std::noboolalpha << std::endl;
// Return type stuff:
std::cout << "Behold, return type sorcery!" << std::endl;
rtCaller(f1);
rtCaller(f2);
rtCaller(f3);
}