2016-04-10 30 views
1

dizgesinden almak için genel bir işlev sarın Genel bir fonksiyonun etrafında bir sarıcı oluşturmak istiyorum T0 f (T1, T2, T3 ...) otomatik olarak bu fonksiyonun argümanlarını bir dizideki dizelerden (dönüştürülmüş) uygun türlere) ve dönüş değeri olan bir şey yapar. Aşağıdaki sözde imsi kodu gibi şeyler yapabiliriz eğer Benim ideal sonuç gerçekleştirilecektir:Bir argümanı

#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; 
} 

ve bu stdout'ta "7" yazdırır.

Bir süre önce SO bakarak bazı günler geçirdi ve şu (Compilable) gibi bir şey ile geldi:

#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 değil. Sadece işlev işaretçisini verilen tuple içerilen argümanlarla çağırır.

Bir süredir bu modelin bir meraklısı sürümünü kullanıyorum (çoğunlukla bir arduino üzerinde çalışan kod hata ayıklamak için otomatik olarak bir arabirim oluşturmak için) ve güzel çalışıyor, ancak kendimi yeniyorum 2 büyük sorun var düzeltmeye çalışıyorum: gereksiz (sarma işleminde işlev adını 3 kez yazdığımda) ve üye işlevleri için çalışmıyor. Bunu düzeltmek mümkün mü?

cevap

2

Birincil sorun, işlev nesnesini oluşturuyor gibi görünüyor. Fonksiyonun tekrarlanan spesifikasyonundan kaçınmak ve üye fonksiyonlarını kapatmak için en kolay yaklaşım, ilgili türleri belirleyen bir fabrika fonksiyonunu kullanmaktır. Bu işlevin uygun şekilde aşırı yüklenmesi, normal ve üye fonksiyonlar arasında kolayca ayırt edilebilir. İşlev türünün yinelenen kullanımının, temsil ve imza türünün gerçekten farklı olduğu işlev nesneleri için olduğunu varsayalım.

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); 
} 

Sen üzerinde üyesi çağırmak için uygun bir nesne sizinIe başa gerekir bu

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); 
} 

gibi wrap() işlevini kullanmak istiyorum ve bazı uzmanlık ihtiyaç muhtemelen yönlendirilirsiniz işlev sarıcı funcWrap. Ayrıca, üye fonksiyon durumu için const üye fonksiyonları için uygun aşırı yüklenmelere sahip olmanız gerekebilir. Üye fonksiyonları için, üyeyi sararken nesneyi belirtmek ve uygun şekilde yakalamak da mantıklı olabilir. std::bind() veya lambda işlev üyesi işlevlerini kullanmak, işlev nesneleri için sarmalayıcıyı kullanarak da bağlanabilir, ancak bu büyük olasılıkla, sonuç olarak ortaya çıkarılan imzanın belirtilmesini gerektirir. Aşağıda

bu sarma çalışması için gerekli tüm bit ve bobs gösteren C++ 11 ile derleme tam demo:

#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

Henüz bu -c + ile, (gcc 4.8 derlemeye alınamıyor + 1y), bir çift yazım hatalarını düzeltmesine rağmen (örneğin, toplayıcı için operatör() (yüzdürme, yüzdürme). Yaklaşımınız bana mantıklı geliyor. Şablonlarla her zamanki gibi beynime komik şeyler yapan detaylar. – user3849418

+0

@ user3849418: Evet, birkaç yazım vardı: Kodu bir cep telefonunda bir araya getirdim. Bu yaklaşımın işe yaradığını gösteren eksiksiz bir uygulama ekledim. –

+0

Oh vay. Ben ... hayranım. Bu, derler ve tam olarak istediğim ve ihtiyaç duyduğum şeyi yapar. Tam olarak şimdi anladım gibi davranmayacağım, ama üzerinde çalışacağım ve bu süreçte çok şey öğreneceğinden şüpheleniyorum. Teşekkür ederim. – user3849418