2016-04-10 3 views
1

Я хочу создать обертку вокруг общей функции, 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; 
} 

и это будет печатать «7» на стандартный вывод.

Некоторое время назад я провел несколько дней, глядя на 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), и он работает хорошо, но у него есть две основные проблемы: я избиваю себя (в том, что я пишу имя функции 3 раза в процессе упаковки), и это не работает для функций-членов. Можно ли исправить это?

ответ

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() или функций-членов лямбда-функции также можно связать с помощью оболочки для объектов-функций, но это, скорее всего, требует указания подписи, которая в противном случае может быть выведена.

Ниже приведен полный демо-компиляции с C++ 11, показывающий все биты, и качается, необходимые, чтобы сделать эту работу оберточной:

#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"; 
} 
+0

Может еще не получить эту компиляцию (GCC 4.8, с -c + + 1y), несмотря на фиксацию пары опечаток (например, operator() (float, float) для сумматора). Однако ваш подход имеет для меня смысл. Это просто детали, которые, как обычно, с шаблонами, делают мои мозги забавные вещи. – user3849418

+0

@ user3849418: да, было несколько опечаток: я скрепил код на мобильном телефоне. Я добавил полную реализацию, показывающую, что этот подход действительно работает. –

+0

О, ничего себе. Я ... в восторге. Это компилирует и делает то, что я просил и нуждаюсь. Я не буду притворяться, что полностью понимаю это сейчас, но я буду работать над этим, и я подозреваю, что в этом процессе вы узнаете много. Спасибо. – user3849418

Смежные вопросы