#include <iostream>     // For std::cout, std::boolalpha, std::endl.
#include <string>
#include <cstdint>      // For int16_t.
#include <type_traits>  // For is_void<T>.

using FilePath = std::string;

// ---------------------
// ExecuteMethod() functor:
// ---------------------

// Default functor.
template<typename... Ts>
struct Executor { };

// General case.
template<typename M, typename ReturnType, typename... Params>
struct Executor<M, ReturnType (*)(Params...)> {
    public:
        // Parameter match:
        bool operator()(M method, Params... params) {
           	ReturnType r = method(params...);
            std::cout << "Valid call.  Return value: " << r << "." << std::endl;
            return true;
        }
        
        // Parameter mismatch:
        template<typename... Invalid_Params>
        bool operator()(M method, Invalid_Params... ts) {
            std::cout << "Invalid call." << std::endl;
            return false;
        }
};

// Special case to catch void return type.
template<typename M, typename... Params>
struct Executor<M, void (*)(Params...)> {
    public:
        // Parameter match:
        bool operator()(M method, Params... params) {
     		method(params...);
            std::cout << "Valid call.  Return value: None, 'cuz it's void." << std::endl;
            return true;
        }
        
        // Parameter mismatch:
        template<typename... Invalid_Params>
        bool operator()(M method, Invalid_Params... ts) {
            std::cout << "Invalid call." << std::endl;
            return false;
        }
};

#define ExecuteMethod(C, M, ...)                    \
    do {                                            \
        Executor<decltype(&M), decltype(&M)> temp;  \
        C = temp(M, ##__VA_ARGS__);                 \
    } while (false)
    
    
// ---------------------
// Example functions:
// ---------------------
    
int GetColor(int16_t* color) { *color = 0xF249; return 42; }
int GetFile(FilePath& file) { std::cout << "You want " << file << "?  You can't handle the file!" << std::endl; return 24; }
int WriteDocument(const FilePath &file, const char *fileFormatName, bool askForParms) { std::cout << "Do it yerself!" << std::endl; return 195; }

// Returns gibberish, to lighten everyone's day.
int f0(int a) { std::cout << "You passed " << a << std::endl; }

bool f1(int& a, const float b) {
	std::cout << ((b > a) ? "b's bigger." : "a's bigger.") << std::endl;
	return (a > b);
}
float f2(float* a, const float* b) { return ++(*a) * (*b); }

char f3() { return 'c'; }

void f4(std::string& s) { s = "A different string."; }

// ---------------------
// main():
// ---------------------

int main() {
    int a = 8;
    float b1 = 2.0, b2 = 3.14;
    bool c = false;
    std::int16_t i16 = 0b1100010100001111; // Suddenly, binary literal!
    std::string s = "A string.";
    FilePath fp = "C:\\Windows\\system32\\chkntfs.exe";

    std::cout << "Testing:" << std::endl;
    std::cout << "- - -" << std::endl;
    std::cout << std::boolalpha;

    std::cout << "(int(*)(int), int):" << std::endl;
    ExecuteMethod(c, f0, a);
    std::cout << "c: " << c << std::endl << std::endl;

    std::cout << "(int(*)(int), int, float):" << std::endl;
    ExecuteMethod(c, f0, a, b1);
    std::cout << "c: " << c << std::endl << std::endl;

    std::cout << "(bool(*)(int&, const float), int&, const float):" << std::endl;
    ExecuteMethod(c, f1, a, b1);
    std::cout << "c: " << c << std::endl << std::endl;
    
    std::cout << "(float(*)(float*, const float*):" << std::endl;
    ExecuteMethod(c, f2, &b1, &b2);
    std::cout << "Well, then.  Once again, with const-ness!" << std::endl;
    std::cout << "Old values: " << b1 << ", " << b2 << std::endl;
    ExecuteMethod(c, f2, &b1, const_cast<const float*>(&b2));
    std::cout << "New values: " << b1 << ", " << b2 << std::endl;
    std::cout << "c: " << c << std::endl << std::endl;

    std::cout << "(char(*)()):" << std::endl;
    ExecuteMethod(c, f3);
    std::cout << "c: " << c << std::endl << std::endl;
    
    std::cout << "Reference testing:" << std::endl;
    std::cout << "(void(*)(std::string&), std::string&):" << std::endl;
    std::cout << "Original string: \"" << s << "\"" << std::endl;
    ExecuteMethod(c, f4, s);
    std::cout << "New string: \"" << s << "\"" <<  std::endl;
    std::cout << "c: " << c << std::endl << std::endl;
    std::cout << std::endl;
    
    std::cout << "Now, let's try your example functions:" << std::endl;
    std::cout << "- - -" << std::endl;
    std::cout << "int GetColor(int16_t*):" << std::endl;
    std::cout << "Old value: " << i16 << std::endl;
    ExecuteMethod(c, GetColor, &i16);
    std::cout << "New value: " << i16 << std::endl;
    std::cout << "c: " << c << std::endl << std::endl;
    
    std::cout << "int GetFile(FilePath&):" << std::endl;
    ExecuteMethod(c, GetFile, fp);
    std::cout << "c: " << c << std::endl << std::endl;
    
    std::cout << "int WriteDocument(const FilePath&, const char*, bool):" << std::endl;
    ExecuteMethod(c, WriteDocument, fp, s.c_str(), true);
    std::cout << "c: " << c << std::endl << std::endl;
    
}