簡體   English   中英

從 std::tuple 中提取類型以獲得方法簽名

[英]Extract types from std::tuple for a method signature

我正在尋找一種方法來提取std::tuple的類型以定義方法簽名。 采取以下(人為的)示例:

template <typename RetT, typename... ArgsT>
class A
{
public:
    typedef RetT ReturnType;
    typedef std::tuple<ArgsT...> ArgTypes;

    RetT doSomething(ArgsT... args)
    {
        // Doesn't make much sense, but it's just an example
        return (RetT) printf(args...);
    }
};

template <typename Enable, typename RetT, typename... ArgsT>
class AAdapter;

// Simply pass arguments along as-is
template <typename RetT, typename... ArgsT>
class AAdapter<std::enable_if_t<!std::is_same_v<RetT, float>>, RetT, ArgsT...> : public A<RetT, ArgsT...> {};

// Add additional first argument if RetT is float
template <typename RetT, typename... ArgsT>
class AAdapter<std::enable_if_t<std::is_same_v<RetT, float>>, RetT, ArgsT...> : public A<RetT, const char*, ArgsT...> {};



template <typename RetT, typename... ArgsT>
class B
{
public:
    typedef AAdapter<void, RetT, ArgsT...> AAdapter;

    // This needs to have the same method signature (return type and argument types) as AAdapter::doSomething()
    template <size_t... Index>
    typename AAdapter::ReturnType doSomething (
        typename std::tuple_element<Index, typename AAdapter::ArgTypes>::type... args
    ) {
        return a.doSomething(args...);
    }

public:
    AAdapter a;
};


int main(int argc, char** argv)
{
    // I would like to be able to remove the <0,1,2> and <0,1,2,3> below.
    B<int, const char*, int, int> b1;
    b1.doSomething<0,1,2>("Two values: %d, %d\n", 1, 2);

    B<float, const char*, int, int> b2;
    b2.doSomething<0,1,2,3>("Three values: %s, %d, %d\n", "a string", 1, 2);

    return 0;
}

考慮 AAdapter 更改、添加或刪除不透明參數類型的方式。 基本上,我希望B::doSomething()簡單地重定向到B::AAdapter::doSomething() ,所以我希望這兩種方法都具有完全相同的簽名。 問題是:如何從 B 內部獲取B B::AAdapter::doSomething()的參數類型?

我在上面的代碼中對B::doSomething()的定義是我來得最遠的:我正在 typedef'ing 一個std::tupleA內部的參數類型,所以我可以將它們解包回B中的參數包. 不幸的是,使用上述方法,我仍然需要在調用B::doSomething()時手動提供Index...模板參數。 當然,必須有一種方法可以從元組的大小中自動推導出這些Index...參數。 我曾考慮過使用std::make_integer_sequence的方法,但這需要我為序列本身定義一個額外的方法參數(並且它不能是具有默認值的最后一個參數,因為在參數包之后不允許使用其他 arguments )。

有沒有什么辦法可以做到這一點,不管有沒有 std::tuple? 需要 C++17 的解決方案會很好。

編輯1:

我現在意識到我可以通過讓BAAdapter繼承而不是讓AAdapter object 作為成員來規避我的特定應用程序中的問題,但我仍然想知道如何解決問題而不必這樣做。

編輯2:

也許有關AAdapter存在的原因以及我想要實現的目標的一些附加信息。 我正在圍繞現有的 C API 實現一種包裝器 class ,實際上需要在另一個進程中調用,RPC 樣式。 因此,如果用戶想在遠程進程中調用 C function ,他們將改為在我的包裝器 class 中調用相應的方法,以處理所有遠程調用和其他丑陋的細節。 這個包裝器 class 在我上面的代碼中由B表示。 現在我的包裝方法簽名通常不會與 C function 具有完全相同的簽名。 例如,包裝器可能具有std::string_view而不是 C function 具有的一對const char*, size_t 由於這里不重要的原因,它還需要有一個 output 參數(一個指針),其中 C function 有時有一個返回值。

為了讓我不必定義兩個單獨的方法簽名(實際上是三個)並編寫代碼來轉換每個單獨的參數,我只將其中一個簽名作為模板參數RetT, ArgsT...傳遞給B 簽名轉換 class (上面示例中的AAdapter )然后應用規則,如何通過添加參數、更改其類型等從第一個簽名自動生成第二個簽名。然后A將持有這個生成的簽名,而B將擁有我最初提供的一個。 但是,我希望B提供帶有A簽名的invoke()方法,從而將A和整個方法簽名完全隱藏在用戶面前。 這就是為什么我需要從B中訪問A的模板參數類型,以及為什么我不能簡單地刪除中間的 class AAdapter的原因。

您問題的核心是將元組轉換為參數包。

也許元組類型不是模板 arguments? 在這種情況下,inheritance 有一個簡單的解決方案:

#include <vector>
#include <iostream>
#include <tuple>

template<typename... Types>
struct BImpl{
    typedef std::tuple<std::vector<Types>...> tuple_type;
    // maybe you will get a tuple type from some class templates. assume the 'tuple_type' is the result.
    // requirement: 'tuple_type' = std::tuple<SomeTypes...>
    // requirement: 'tuple_type' can be deduced definitely from template arguments 'Types...'.
    template<typename> // you can add other template arguments, even another std::tuple.
    struct OptCallHelper;
    template<typename... Args>
    struct OptCallHelper<std::tuple<Args...>>{
        auto dosomething(Args&&... args) /* const? noexcept? */{
            // do what you want...
            // requirement: you can definitely define the 'dosomething' here without any other informations.
            std::cout << "implement it here." << std::endl;
        }
    };
    typedef OptCallHelper<tuple_type> OptCall;
};

template<typename... Types>
struct B : private BImpl<Types...>::OptCall{
    typedef typename BImpl<Types...>::OptCall base;
    using base::dosomething;
    // obviously, you can't change the implementation here.
    // in other words, the definition of 'dosomething' can only depend on template arguments 'Types...'.
};


int main(){
    B<int, float> b;
    b({}, {}); // shows "implement it here."
    return 0;
}

你可以在BImpl中做你想做的事,然后用B代替。

 // This needs to have the same method signature (return type and argument types) as AAdapter::doSomething() template <size_t... Index> typename AAdapter::ReturnType doSomething ( typename std::tuple_element<Index, typename AAdapter::ArgTypes>::type... args ) { return a.doSomething(args...); }

對於AAdaptor ,我想你只想要A中的dosomething的接口,你可以推導出來:

#include <iostream>

template<typename...>
struct AAdaptor{
    int dosomething(){
        std::cout << "???" << std::endl;
        return 0;
    }
};
// ignore the implementation of AAdaptor and A.
// just consider of how to get the interface of 'dosomething'.

template<typename... Types>
struct BImpl{
    typedef AAdaptor<Types...> value_type;
    typedef decltype(&value_type::dosomething) function_type;
    // attention: it won't work if 'AAdaptor::dosomething' is function template or overloaded.
    //            in this case, you should let A or AAdaptor give a lot of tuples to declare 'dosomething', referring to the first solution.

    

    template<typename>
    struct OptCallHelper;
    template<typename Ret, typename Klass, typename... Args>
    struct OptCallHelper<Ret(Klass::*)(Args...)>{
        value_type data;
        Ret dosomething(Args... args){
            return data.dosomething(args...);
        }
    };
    // attention: 'Ret(Klass::*)(Args...)' is different from 'Ret(Klass::*)(Args...) const', 'noexcept' as well in C++17.
    //            even Ret(Klass::*)(Args..., ...) is also different from them.
    //            you have to specialize all of them.

    typedef OptCallHelper<function_type> OptCall;
};

template<typename... Types>
struct B : BImpl<Types...>::OptCall{
    typedef typename BImpl<Types...>::OptCall base;
    using base::dosomething;
};


int main(){
    B<int, float> b;
    b(); // shows "???"
    return 0;
}

如果此代碼與您的要求之間存在一些差異,請嘗試舉另一個示例來暗示您的一些實現。 目前還不清楚B得到什么和應該做什么。

這演示了如何從元組中獲取帶有參數類型的 function:

#include <iostream>
#include <tuple>
#include <utility>

template <
        typename ArgTuple
>
class B_Class {};

template <typename... ArgTypes>
class B_Class<std::tuple<ArgTypes...> > {
public:
        static void b(
                ArgTypes...
        ) {
                std::cout << "successful call" << std::endl;
        }
};

int main() {
        using ArgTypes = std::tuple<int, char, float, double>;
        int i; char c; float f; double d;
        B_Class<ArgTypes>::b(i, c, f, d);
}

這會在運行時編譯並打印“成功調用”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM