包装一般函数以从字符串

时间:2016-04-10 17:44:37

标签: c++ variadic-templates

我希望自动创建一个通用函数T0 f(T1,T2,T3 ...)的包装器,它从堆栈中的字符串中提取该函数的参数(转换为适当的类型),并且用返回值做某事。如果我可以执行类似以下伪代码的操作,那么我的理想结果将会实现:

#include <stack>
std::stack<char*> argStack;
std::stack<char*> retStack;


int add(float a, float b){
    return a+b;
}
int main(){
    argStack.push((char*)"2");
    argStack.push((char*)"5");
    auto wrapped=funcWrap(add);
    wrapped();
    std::cout << retStack.top();
    return 0;
}

这将打印&#34; 7&#34;在stdout上。

前段时间我花了几天时间看着SO并想出了类似下面的内容(可编辑):

#include <iostream>
#include <stack>
#include <string>
#include <sstream>
#include <tuple>
#include "apply_tuple.h"
using std::cout;
using std::endl;
using std::string;
std::stack<string> argStack;
std::stack<string> retStack;

template<typename T>
T getNextArg(){
    std::stringstream ss;
    ss.str(argStack.top());
    argStack.pop();
    T convertedVar;
    ss >> convertedVar;
    return convertedVar;
}

template <typename Fn, typename funcSig> struct funcWrap;  

template <typename Fn, typename R, typename ...Args>
struct funcWrap<Fn, R(Args...)>       // specialized for typename = R(Args...)
{
    using funcType= Fn;
    funcType *wrappedFunc;

    funcWrap(funcType &inputFunc){
        this->wrappedFunc=&inputFunc;
    }
    void operator()(){
        auto tup =std::make_tuple(getNextArg<Args>()...);
        std::stringstream ss;
        ss << apply_tuple(*wrappedFunc, tup);
        retStack.push(ss.str());
    }
};

int add(float a, float b){
    return a+b;
}

int main(){
    argStack.push(string("2"));
    argStack.push(string("5"));
    auto wrapped=funcWrap<decltype(add), decltype(add)>(add);
    wrapped();
    cout << retStack.top();
    return 0;
}

apply_tuple来自https://www.preney.ca/paul/archives/1099。它只是使用提供的元组中包含的参数调用函数指针。

我一直在使用这种模式的更高版本(主要是自动生成调试arduino上运行的代码的界面),它运行良好,但它有两个主要问题我&#39;我试图解决问题:它是多余的(因为我在包装过程中写了3次函数名),它对成员函数不起作用。有可能解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

看来你的主要问题是构建函数对象。避免重复指定函数和覆盖成员函数的最简单方法是使用推导相关类型的工厂函数。适当地重载此函数可以很容易地区分正常函数和成员函数。我假设函数类型的重复使用是用于表示类型和签名实际上不同的函数对象。

template <typename R, typename... A>
funcWrap<R(A...), R(*)(A...)>
wrap(R(*fun)(A...)) { // deal with functions
    return funcWrap<R(A...), R(*)(A...)>(fun);
}
template <typename Signature, typename F>
funcWrap<Signature, F>
wrap(F&& f) { // deal with function objects
    return funcWrap<Signature, F>(std::forward<F>(f));
}
template <typename R, typename S, typename... A>
funcWrap<R(S&, A...), R (S::*)(A...)>
wrap(R(S::*mem)(A...)) {
    return funcWrap<R(S&, A...), R (S::*)(A...)>(mem);
}

你可以像这样使用wrap()函数

 float add(float, float);
 struct adder { float operator()(float, float); };
 struct foo { foo mem(foo); };

 int main() {
     auto wfun = wrap(&add);
     auto wobj = wrap<float(float, float)>(adder());
     auto wmem = wrap(&foo::mem);
 }

你需要处理一个合适的对象来调用你的成员,你可能需要一些函数包装器funcWrap的特化。此外,对于成员函数案例,您可能需要为const成员函数提供合适的重载。对于成员函数,在包装成员并适当捕获它时指定对象也是有意义的。使用std::bind()或lambda函数成员函数也可以使用函数对象的包装器进行绑定,但这很可能需要指定可以推导出的签名。

下面是一个用C ++ 11编译的完整演示,显示了使这个包装工作所需的所有位和bob:

#include <iostream>
#include <sstream>
#include <stack>
#include <string>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
#include <cstddef>

// ----------------------------------------------------------------------------

template <typename T>
typename std::decay<T>::type makeArg(std::stack<std::string>& args) {
    typename std::decay<T>::type rc;
    if (args.empty() || !(std::istringstream(args.top()) >> rc)) {
        throw std::runtime_error("can't create argument from '" + (args.empty()? "<empty>": args.top()) + "'");
    }
    args.pop();
    return rc;
}

// ----------------------------------------------------------------------------

namespace util
{
    template<typename T, T...>
    struct integer_sequence {
    };
    template<std::size_t... I>
    using index_sequence = integer_sequence<std::size_t, I...>;

    template <typename T, std::size_t N, T... I>
    struct integer_sequencer {
        using type = typename integer_sequencer<T, N - 1, N - 1, I...>::type;
    };
    template <typename T, T... I>
    struct integer_sequencer<T, 0, I...> {
        using type =  integer_sequence<T, I...>;
    };

    template<typename T, T N>
    using make_integer_sequence = typename integer_sequencer<T, N>::type;
    template<std::size_t N>
    using make_index_sequence = make_integer_sequence<std::size_t, N>;

    template <typename F, typename T, std::size_t... I>
    auto apply_aux(F&& fun, T&& tuple, index_sequence<I...>) -> decltype(fun(std::get<I>(tuple)...)) {
        return fun(std::get<I>(tuple)...);
    }

    template <typename F, typename T>
    auto apply(F&& f, T&& t)
        -> decltype(apply_aux(std::forward<F>(f), std::forward<T>(t), make_index_sequence<std::tuple_size<typename std::decay<T>::type>::value>())) {
        return apply_aux(std::forward<F>(f), std::forward<T>(t), make_index_sequence<std::tuple_size<typename std::decay<T>::type>::value>());
    }
}

// ----------------------------------------------------------------------------

template <typename S, typename F> class funcWrap;

template <typename R, typename... A, typename F>
class funcWrap<R(A...), F> {
private:
    F fun;

public:
    funcWrap(F fun): fun(fun) {}

    std::string operator()(std::stack<std::string>& args) {
        std::tuple<typename std::decay<A>::type...> t{ makeArg<A>(args)... };
        std::ostringstream out;
        out << util::apply(this->fun, t);
        return out.str();
    }
};

template <typename R, typename... A, typename S, typename... B>
class funcWrap<R(A...), R (S::*)(B...)> {
private:
    R (S::*mem)(B...);

public:
    funcWrap(R (S::*mem)(B...)): mem(mem) {}

    std::string operator()(std::stack<std::string>& args) {
        std::tuple<typename std::decay<A>::type...> t{ makeArg<A>(args)... };
        std::ostringstream out;
        out << util::apply([=](S& s, B... b){ return (s.*(this->mem))(b...); }, t);
        return out.str();
    }
};

// ----------------------------------------------------------------------------

template <typename R, typename... A>
funcWrap<R(A...), R(*)(A...)>
wrap(R(*fun)(A...)) { // deal with functions
    return funcWrap<R(A...), R(*)(A...)>(fun);
}
template <typename Signature, typename F>
funcWrap<Signature, F>
wrap(F&& f) { // deal with function objects
    return funcWrap<Signature, F>(std::forward<F>(f));
}
template <typename R, typename S, typename... A>
funcWrap<R(S&, A...), R (S::*)(A...)>
wrap(R(S::*mem)(A...)) {
    return funcWrap<R(S&, A...), R (S::*)(A...)>(mem);
}

float add(float f0, float f1) { return f0 + f1; }
struct adder {
    float value;
    explicit adder(float value): value(value) {}
    float operator()(float f0, float f1) {
        return value + f0 + f1;
    }
};
struct foo {
    float value;
    foo(): value() {}
    explicit foo(float value): value(value) {}
    foo mem(foo f) { return foo(value + f.value); }
};

std::istream& operator>> (std::istream& in, foo& f) {
    return in >> f.value;
}
std::ostream& operator<< (std::ostream& out, foo const& f) {
    return out << f.value;
}

int main() {
    std::stack<std::string> args;

    auto wfun = wrap(&add);
    args.push("17.5");
    args.push("42.25");
    std::cout << "wfun result=" << wfun(args) << "\n";

    auto wobj = wrap<float(float, float)>(adder(3.125));
    args.push("17.5");
    args.push("42.25");
    std::cout << "wobj result=" << wobj(args) << "\n";

    auto wmem = wrap(&foo::mem);
    args.push("17.5");
    args.push("42.25");
    std::cout << "wmem result=" << wmem(args) << "\n";
}