1

Based on this article, I tried to create a generic parser, which outputs the return value of the called function as a string.

Parsing std::vector of std::strings into std::tuple of arbitrary types

Unfortunately, I didn't figure out, how to format the return value of a function which returns void...

template<class R, class T, class... Args> class CCmd {
public:
    CCmd(R (T::* fptr)(Args...), T * obj){}

    auto call(Args&&... args) -> R {

        auto func = std::mem_fun(mFunction);

        return func(mObject, std::forward<Args>(args)...);
    }

protected:

    R (T::* mFunction)(Args...);

    T * mObject;
};

And:

template< class R, class T, class... Args> class CCommand : public CCmd<R,T,Args...> {

public:
    CCommand(R (T::* fptr)(Args...), T * obj) :
        CCmd<R,T,Args...>(fptr, obj) {
    }

    void Execute(std::vector<std::string> && parameters, std::string & returnValue) {
        if(parameters.size() >= std::tuple_size<decltype(args)>::value) {

            std::stringstream stream;

            Parse(std::integral_constant<std::size_t, std::tuple_size<decltype(args)>::value - 1>{}, std::forward<decltype(args)>(args), std::forward<std::vector<std::string>>(parameters));

            /* the following line fails, if R is of type void */
            stream << CallFunc(typename GenerateArgumentIndexPack<std::tuple_size<decltype(args)>::value>::Pack());

            stream >> returnValue;

        } else {
            returnValue = "not enough parameters";
        }
    }




protected:

    std::tuple<Args...> args;

    template <typename X, typename Y>
    void Fill(const Y && input, X & output) {
        std::stringstream stream;
        stream << input;
        stream >> output;
    }

    template<std::size_t N, typename... Ts>
    void Parse(std::integral_constant<std::size_t, N>, std::tuple<Ts...>&& info, std::vector<std::string>&& tokens) {
        Fill(std::forward<std::string>(tokens[N]), std::get<N>(info));
        Parse(std::integral_constant<std::size_t, N - 1>{}, info, tokens);
    }

    template<typename... Ts>
    void Parse(std::integral_constant<std::size_t, 0>, std::tuple<Ts...>&& info, std::vector<std::string>&& tokens) {
        Fill(std::forward<std::string>(tokens[0]), std::get<0>(info));
    }

    template <std::size_t... ArgumentIndexes>
    struct ArgumentIndexPack {};

    template <std::size_t NumberOfArgumentIndexesToGenerate, std::size_t... GeneratedArgumentIndexes>
    struct GenerateArgumentIndexPack : GenerateArgumentIndexPack<NumberOfArgumentIndexesToGenerate - 1, NumberOfArgumentIndexesToGenerate - 1, GeneratedArgumentIndexes...> {};

    template <std::size_t... GeneratedArgumentIndexes>
    struct GenerateArgumentIndexPack<0, GeneratedArgumentIndexes...> {
        using Pack = ArgumentIndexPack<GeneratedArgumentIndexes...>;
    };

    template <std::size_t... ArgumentIndexes>
    auto CallFunc(ArgumentIndexPack<ArgumentIndexes...>) -> R {
        return CCmd<R,T,Args...>::call(std::forward<Args>(std::get<ArgumentIndexes>(args))...);
    }
};

A class:

class CMyClass {
public:
    void voidFunc() {
        std::cout << "CMyClass::voidFunc" << std::endl;
    }

    void voidDoubleFunc(double d) {
        std::cout << "CMyClass::voidDoubleFunc(" << d << ")" << std::endl;
    }
};

main:

int main(int argc, char** argv) {

    CMyClass oMyClass;
    CCommand<void, CMyClass> testObj0(&CMyClass::voidFunc,        &oMyClass);
    CCommand<void, CMyClass, double> testObj3(&CMyClass::voidDoubleFunc,  &oMyClass);

    std::string retval;

    // This fails as get on a tuple with 0 elements seems to be invalid
    testObj0.Execute({}, retval);
    // This doesn't compile, as the voidDoubleFunc returns void
    testObj3.Execute({"1.23", retval);

    return 0;
}

Compiler call:

g++ -c src/main.cpp -o src/main.o -std=c++11

Error message (1) when calling Execute on a R T::f() where R is not of type void:

src/main.cpp: In instantiation of 'void CCommand<R, T, Args>::Parse(std::integral_constant<long long unsigned int, N>, std::tuple<_Args2 ...>&&, std::vector<std::basic_string<char> >&&) [with long long unsigned int N = 18446744073709551615ull; Ts = {}; R = void; T = CMyClass; Args = {}]':
src/main.cpp:73:179:   required from 'void CCommand<R, T, Args>::Execute(std::vector<std::basic_string<char> >&&, std::string&) [with R = void; T = CMyClass; Args = {}; std::string = std::basic_string<char>]'
src/main.cpp:147:31:   required from here
src/main.cpp:98:68: error: no matching function for call to 'get(std::tuple<>&)'
         Fill(std::forward<std::string>(tokens[N]), std::get<N>(info));

Error message (2) when calling Execute on a void T::f(Args...) where Args... is non void:

src/main.cpp: In instantiation of 'void CCommand<R, T, Args>::Execute(std::vector<std::basic_string<char> >&&, std::string&) [with R = void; T = CMyClass; Args = {double}; std::string = std::basic_string<char>]':
src/main.cpp:156:43:   required from here
src/main.cpp:76:11: error: no match for 'operator<<' (operand types are 'std::stringstream {aka std::basic_stringstream<char>}' and 'void')
    stream << CallFunc(typename GenerateArgumentIndexPack<std::tuple_size<decltype(args)>::value>::Pack());
Community
  • 1
  • 1
PirklW
  • 484
  • 2
  • 16
  • 1
    What's the question? This is just a bunch of code that compiles. What doesn't work the way you want it to? – Barry Mar 09 '16 at 15:29
  • Sorry, just edited some code. First of all, it doesn't compile, as Execute tries to call stream << void; Second, it doesn't compile as get is called on a tuple with no elements – PirklW Mar 09 '16 at 15:34
  • @WernerPirkl that sould be part of the post, not a comment. Also: what compiler and flags do you use and what is the error message you get? – Ilya Popov Mar 09 '16 at 16:37
  • OK, I added the required information, hope somebody has an idea – PirklW Mar 10 '16 at 07:02

1 Answers1

1

For your first problem, you simply need to create and overload for Parse:

void Parse(std::integral_constant<std::size_t, -1>, 
           std::tuple<>&& info, std::vector<std::string>&& tokens) {
}

For your second problem (method returning void), you could use an intermediate struct for the result and specialize CCmd for void:

Intermediate struct ReturnOf:

template <typename R>
struct ReturnOf {
    typedef R return_type;
};

template <>
struct ReturnOf<void> {
    typedef std::string return_type;
};

Specialization of CCmd for void:

template<class T, class... Args> class CCmd<void, T, Args...> {
public:
    CCmd(void (T::* fptr)(Args...), T * obj) : mFunction(fptr), mObject(obj) { }

    auto call(Args&&... args) -> std::string {
        auto func = std::mem_fun(mFunction);
        func(mObject, std::forward<Args>(args)...);
        return std::string();
    }

protected:
    void (T::* mFunction)(Args...);
    T * mObject;
};

And finally in CCommand:

template <std::size_t... ArgumentIndexes>
auto CallFunc(ArgumentIndexPack<ArgumentIndexes...>) 
    -> typename ReturnOf<R>::return_type {
    ...
}
Holt
  • 36,600
  • 7
  • 92
  • 139