2017-05-29 71 views
3

我有一些的3rdParty庫這樣的方法:隱藏可變參數模板實施

bool Invoke(const char* method, Value* args, size_t nargs) 

它需要其內類型的數組(可轉換至任何原語C++類型)和Arg計數作爲其內PARAMS。 在我的代碼,我寫了一些通用的輔助,以避免每個調用手動創建和類型的皈依:

template<class ... Args> 
bool WrappedValue::Invoke(const char* method, Args&& ... args) 
{ 
    3rdParty::Value values[] = 
    { 
     3rdParty::Value(std::forward<Args>(args))... 
    } 

    return m_value.Invoke(method, values, sizeof ... (Args)); 
} 

它工作得很好,但現在我要在我的頭文件中定義的3rdParty代碼和lib直接連接到我的主要項目。

是否可以隱藏該第三方庫的實現細節和用法? (使用某種疙瘩成語或代理對象3rdParty ::價值)。我知道無法在C++中使用虛擬模板方法來創建代理或僅將模板實現移動到.cpp,因此我完全停留在這個問題上。

將是任何幫助感激)

回答

2

當然。簡單的寫下相當於std::variant<int, double, char, every, other, primitive, type>

現在您的Invoke將您的args轉換爲這些變體的數組(矢量,跨度,任何)。

然後你將這個變量數組傳遞給你的內部調用方法。

該內部調用方法然後使用等效的std::visit從每個變體中生成3rdParty::Value

Boost提供了一個可能工作的boost::variant

你也可以手動滾動。通過狹隘地指出你的問題,你會得到比std::variant更簡單的東西。但是,這不僅僅是一點工作。


另一種方法是這樣的

template<class T> struct tag_t {constexpr tag_t(){}; using type=T;}; 
template<class T> constexpr tag_t<T> tag{}; 

template<class T, class F, class ... Args> 
bool WrappedValue::Invoke(tag_t<T>, F&& f, const char* method, Args&& ... args) 
{ 
    T values[] = { 
    T(std::forward<Args>(args))... 
    }; 

    return std::forward<F>(f)(method, values, sizeof...(Args)); 
} 

這是簡單的。在這裏,你會寫:

bool r = Invoke(tag<3rdParty::Value>, [&](const char* method, 3rdParty::Value* values, std::size_t count) { 
    m_value.Invoke(method, values, count); 
}, 3.14, 42, "hello world"); 
+0

你的第二個方法公開的3rdParty API,即'的3rdParty :: Value'在實際'的invoke()'調用,其中OP希望避免。 – Walter

+0

@Walter第一種方法隱藏調用者的第三方API,第二種方法本身。我不確定OP是想要的;這是「主要項目」。所以我包含了兩個選項。 – Yakk

1

如果你想避免暴露的3rdParty API,你需要一些非模板的方法來傳遞數據。這不可避免地需要一些類型擦除機制(如std::any),而這些機制將暴露在您的API中。

所以,你能做到這一點,但隨後的3rdParty Value已經是一個類型擦除方法,這隻會從一種類型擦除的數據傳遞到下一個,產生額外的開銷。無論這個價格是否值得付費,只有您可以決定。

我不知何故忽略了你的評論,即參數都是原始的。在這種情況下,類型擦除要簡單得多,可以通過標記+聯合來完成,如

struct erasure_of_primitive 
{ 
    enum { is_void=0, is_str=1, is_int=2, is_flt=3, is_ptr=4 } 
    int type = is_void; 
    union { 
    const char*s; // pointer to external C-string 
    int64_t i;  // any integer 
    double d;  // any floating point number 
    void*p;  // any pointer 
    } u; 

    erasure_of_primitive() = default; 
    erasure_of_primitive(erasure_of_primitive&const) = default; 
    erasure_of_primitive&operator=(erasure_of_primitive&const) = default; 

    erasure_of_primitive(const char*str) 
    : type(is_str), u.s(str) {} 

    template<typename T> 
    erasure_of_primitive(T x, enable_if_t<is_integer<T>::value>* =0) 
    : type(is_int), u.i(x) {} 

    template<typename T> 
    erasure_of_primitive(T x, enable_if_t<is_floating_point<T>::value>* =0) 
    : type(is_flt), u.d(x) {} 

    template<typename T> 
    erasure_of_primitive(T*x) 
    : type(is_ptr), u.p(static_cast<void*>(x)) {} 
};