2017-07-11 20 views
6

Son birkaç günü, C++ işlev işaretçileri için genelleştirilmiş bir sarmalayıcı oluşturmaya çalıştım ve neredeyse her bir sorun çubuğunu tek tek çözmeyi başardım. Bununla temel amacım, bir nesneyi dahili olarak depolanmış bir işlev göstericisiyle bir işlev olarak basitçe çağırmaktı. İşaretçi bir yere işaret ediyorsa, boş bir işaretçi işlevi çağırmaz ve hiçbir şey olmamış gibi devam ederse normal olarak çağırır. Öncelikli olarak, işlev çağrısı yapılsaydı ve sadece bir eylemi önceden oluşturmak istediğimde umurumda olmayacak olan geri arama işlevi amaçları için kullanmayı düşünmekteyim. Aşağıdaki neredeyse mükemmel çalışıyor:C++ '...' parametre paketinin C++ şablon değeri olarak kullanılması?

template<typename T> 
class Action; 

template<typename TReturn, typename ... TArgs> 
class Action<TReturn(TArgs...)> { 
public: 
    //! Define a type for the Action object 
    typedef TReturn(*signature)(TArgs...); 

    //! Constructors 
    inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {} 
    inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {} 

    //! Operator Call 
    inline bool operator() (TReturn& pReturn, TArgs ... pArgs) const { if (!mPointer) return false; pReturn = mPointer(pArgs...); return true; } 

    //! Operators 
    inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; } 
    inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; } 
    inline operator bool() const { return (mPointer != nullptr); } 

private: 
    //! Store a pointer to the callback function 
    signature mPointer; 
}; 

template<typename ... TArgs> 
class Action<void(TArgs...)> { 
public: 
    //! Define a type for the Action object 
    typedef void(*signature)(TArgs...); 

    //! Constructors 
    inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {} 
    inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {} 

    //! Operator Call 
    inline bool operator() (TArgs ... pArgs) const { if (!mPointer) return false; mPointer(pArgs...); return true; } 

    //! Operators 
    inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; } 
    inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; } 
    inline operator bool() const { return (mPointer != nullptr); } 

private: 
    //! Store a pointer to the callback function 
    signature mPointer; 
}; 

Ancak, bu sarıcı ayıklama bilgi veya biçimlendirilmiş metin çıktısı olan en olası kullanım durumu gibi hissediyorum. Bu, kullanıcı tanımlı işlevler veya printf gibi dahili işlevler aracılığıyla olabilir. printf imzası ile eşleştirmek için bir Eylem gibi yaratılmış olacaktır:

Action<int(const char*, ...)> callback = printf; 

ve herhangi başka bir Eylem davranacaklarını aynı şekilde işletmek mümkün olacaktır. Bulduğum problem "...", şablon imzasını uzmanlıklardan biriyle hizalamamaya zorlayacak, bunun yerine sadece bir prototip olan ilk adımı atacak.

Bunun neden işe yaramadığını ve derleyicinin neden gerekli sınıfın üretimiyle başa çıkamayacağını tam olarak anlayabiliyorum, ancak buradaki birinin ya buna benzer bir şey elde etmenin herhangi bir gizli yolu olduğunu umuyordum. Sorunuzun söz altında yorumlandığı gibi

#include <utility> 
#include <cstdio> 
#include <cmath> 

template<typename Fn> 
class Action { 
    Fn* function_ptr;   
public: 
    Action() noexcept : function_ptr(nullptr) {}  
    Action(std::nullptr_t) noexcept : function_ptr(nullptr) {}  
    Action(const Action& other) : function_ptr(other.function_ptr) {}  
    Action(Fn f) : function_ptr(f) {} 

    Action& operator=(const Action& other) { 
     return (function_ptr = other.function_ptr, *this); 
    }    
    Action& operator=(std::nullptr_t) { 
     return (function_ptr = nullptr, *this); 
    }  
    Action& operator=(Fn f) { 
     return (function_ptr = f, *this); 
    } 

    template<typename... Params> 
    auto operator()(Params&&... params) { 
     return function_ptr(std::forward<Params>(params)...); 
    } 
}; 

Live Demo

: Herhangi bir yardım çok thanks :)

+7

Neden std :: function' kullanılmıyor? – StoryTeller

+0

"Eylem" in amacı nedir? Hangi gereksinimi ['std :: function'] kullanamazsınız (http://en.cppreference.com/w/cpp/utility/functional/function)? Hangi sorun '' eylem' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' –

+1

https://stackoverflow.com/questions/18370396/why-cant-stdfunction-bind-to-c-style-variadic-functions, neredeyse bunun nasıl gerçekleştirileceğine dair bir yorumda bulundu. Her bir değişken fonksiyon için bence size yardımcı olacak somut bir sınıfa ihtiyaç duyacaksınız ve umuyoruz ki, aradığınız fonksiyonun v * varyantı var. – mksteve

cevap

1

Aşağıdaki örnek hiçbir yakalama ile tüm işlev türleri için çalışır ve lambda'lar, mutluluk duyacağız std::function kullanarak, işlev işaretçileri için bir sarıcı yazmaktan daha iyidir. std::function ile ilgili tek sorun, elips içeren işlev imzaları ile kullanılamaz olmasıdır. İmzayı açıkça belirtirseniz, ör. printf şöyle: Eğer C++ 17 veya Boost kullanırsanız

std::function<int(const char*, int, double, double)> fn = printf; 

, aşağıdaki gibi std::any veya std::function atanabilir Boost.Any kullanarak, sen printf benzeri işlevini kendi uygulayabilirsiniz:

#include <iostream> 
#include <string> 
#include <vector> 
#include <any> 
#include <functional> 

using namespace std; 

void any_printf(string&& format, vector<any>&& args) { 
    int arg_index = 0; enum { NORMAL, CONVERT } mode; 

    for(auto& chr : format) { 
     if(mode == CONVERT) { 
      switch(chr) { 
      case 'd': cout << any_cast<int>(args[arg_index++]); break; 
      case 'f': cout << any_cast<float>(args[arg_index++]); break; 
      case 's': cout << any_cast<string>(args[arg_index++]); break; 
      /* ... */ 
      default: cout << chr; 
      }; 
      mode = NORMAL; 
     } 
     else { 
      chr == '%' ? (mode = CONVERT, 0) : (cout << chr, 0); 
     } 
    } 
} 

int main() { 
    using namespace string_literals; 
    function<void(string&&, vector<any>&&)> f_ptr { any_printf }; 

    f_ptr("Cuboid: %smm x %dmm x %fmm.\n", { any("3"s), any(4), any(6.67f) }); 
    return 0; 
} 
+0

kontrolleri için ihtiyacım ortadan kaldırmak için yapıldı. Bu işe almak için ilginç bir yoldur.Bununla ilgili tek sorunum, şablonun imzasını almak için var olması gereken bir değer gerektirmesi. İstenen fonksiyon geri çağırmalarının basit bir şekilde yavaşlatılması, durumu en azından tek bir işlev olmaksızın, duruma uyacak kadar kolay değildir. Ben imkansız olmasaydı, yakında olacak olan sözdiziminin tam tipini tahmin etmiştim. Hızlı cevap için teşekkürler :) – MitchellCroft

+1

@MitchellCroft, bir geri çağırmaya çağıracak bir işlev yazdığınızda, onun imzasını tanımlamanız gerekir. Eğer ** bu geri bildirimi değişken veri sayısıyla ve değişken tiplerle ** çağırmak isterseniz ** (printf' gibi) çağrılabilir. ['std :: any'] (http://en.cppreference.com/w/cpp/utility/any) türünde bir' std :: vektörü '. Bu bir C++ 17 özelliğidir, ancak [Boost.Any] (http://www.boost.org/doc/libs/1_64_0/doc/html/any.html) de bir ikame olarak kullanılabilir. – Akira

+0

@MitchellCroft, cevabımı, önceki yorumda bahsettiğim 'std :: any' yöntemiyle düzenledim. – Akira