簡體   English   中英

C++ 模板以避免長開關,同時調用具有不同返回類型的 function

[英]C++ templates to avoid long switches, while calling a function with different return types

我有許多函數q1q2q3等,每個函數都有不同的返回類型( intint64_tstd::string等)。

我也有一個print_result function 打印出他們的結果(以及他們運行的時間,但為了簡單起見在這里修剪):

template <typename T>
void print_result(T (*func)()) {
  T res = func();
  std::cout << res << std::endl;
}

我也有很大的 switch 語句來打印每個函數的結果:

switch (question_num) {
  case 1: print_result(q1); break;
  case 2: print_result(q2); break;
  case 3: print_result(q3); break;
  // ...
}

目標:我想用模板 function 替換這個 switch 語句,以避免每次添加新的 function 時復制每一行。

我試圖查看C++ 模板實例化:避免長開關,但我是模板元編程的新手,所以不知道如何准確處理這個問題。

我目前無法編譯的嘗試:


template <<int, typename> ...> struct FuncList {};

template <typename T>
bool handle_cases(int, T, FuncList<>) {
  // default case
  return false;
}

template <<int I, typename T> ...S>
bool handle_cases(int i, T (*func)(), FuncList<T, S...>) {
  if (I != i) {
    return handle_cases(i, func, FuncList<S...>());
  }
  print_result(func);
  return true;
}

template <typename ...S>
bool handle_cases(int i, T (*func)()) {
  return handle_cases(i, func, FuncList<S...>());
}

// ...
  bool res = handle_cases<
    <1, q1>, <2, q2>, <3, q3>
  >(question_num);
// ...

我使用這個模板的理想方式顯示在最后一行。

請注意,此處提供了從 function 編號到 function 的映射。 function 數字是固定的,即q1映射到常數1 ,並且在運行時不會改變。

編譯錯誤(它可能相當基本,但我真的不太了解元編程):

error: expected unqualified-id before ‘<<’ token
   17 | template <<int, typename> ...> struct FuncList {};
      |          ^~

我有一個不同的建議:

  1. 使用 std::array 代替開關(或 std::map 如果開關情況不連續,std::array 具有 O(1) 訪問時間,std::map O(log(n)) 和開關 O (n)。
  2. 使用 std::function 和 std::bind 將要調用的函數綁定到仿函數 object
  3. 使用數組中的索引來調用 function
  4. 如果您需要傳遞其他數據,請使用占位符
#include <iostream>
#include <functional>

template <typename T>
void print_result(T (*func)()) {
  T res = func();
  std::cout << res << std::endl;
}

int int_function() {
    return 3;
}

double double_function() {
    return 3.5;
}

std::array<std::function<void()>, 2> functions({
    std::bind(print_result<int>, int_function),
    std::bind(print_result<double>, double_function),
});

int main() {

    functions[0]();
    functions[1]();

    return 0;
}

Output:

3
3.5

請參閱: 為什么 std::function 可以隱式轉換為具有更多參數的 std::function?

更新:

通過參數傳遞:

#include <iostream>
#include <functional>

template <typename T>
void print_result(T (*func)(int), int value) {
  T res = func(value);
  std::cout << res << std::endl;
}

int int_function(int value) {
    return 3 * value;
}

double double_function(int value) {
    return 3.5 * value;
}

std::array<std::function<void(int)>, 2> functions({
    std::bind(print_result<int>, int_function, std::placeholders::_1),
    std::bind(print_result<double>, double_function, std::placeholders::_1),
});

int main() {

    functions[0](10);
    functions[1](11);

    return 0;
}

Output:

30
38.5

您可能喜歡不需要任何類型的運行時容器、中間不生成任何對象、甚至不生成數據表、生成的代碼非常少且易於使用的版本:

// Example functions
int fint() { return 1; }
double fdouble() { return 2.2; }
std::string fstring() { return "Hallo"; }

// your templated result printer
     template < typename T>
void print_result( T parm )
{
    std::cout << "The result of call is " << parm << std::endl;
}

// lets create a type which is able to hold functions
template < auto ... FUNCS >
struct FUNC_CONTAINER
{
    static constexpr unsigned int size = sizeof...(FUNCS);
};

// and generate a interface to switch
template < unsigned int, typename T >
struct Switch_Impl;


template < unsigned int IDX, auto HEAD, auto ... TAIL >
struct Switch_Impl< IDX, FUNC_CONTAINER<HEAD, TAIL...>>
{
    static void Do( unsigned int idx )
    {
        if ( idx == IDX )
        {
            // Your function goes here
            print_result(HEAD());
        }
        else
        {
            if constexpr ( sizeof...(TAIL))
            {
                Switch_Impl< IDX+1, FUNC_CONTAINER<TAIL...>>::Do(idx);
            }
        }
    }
};

// a simple forwarder to simplify the interface
template < typename T>
struct Switch
{
    static void Do(unsigned int idx )
    {
        Switch_Impl< 0, T >::Do( idx );
    }
};

// and lets execute the stuff
int main()
{
    using FUNCS = FUNC_CONTAINER< fint, fdouble, fstring >;

    for ( unsigned int idx = 0; idx< FUNCS::size; idx++ )
    {
        Switch<FUNCS>::Do(idx);
    }
}
                                                                                                                                                                  

如果您可以使用 c++17,這是@Klaus 方法的“簡化”版本。 您可以使用 c++17 折疊表達式,而不是使用已經制作的遞歸結構:

template<auto... Funcs, std::size_t... I>
bool select_case(std::size_t i, std::integer_sequence<std::size_t, I...>) {
    return ([&]{ if(i == I) { print_result(Funcs); return true; } return false; }() || ... ); 
}

template<auto... Funcs>
struct FuncSwitch {

    static bool Call(std::size_t i) {
        return select_case<Funcs...>(i, std::make_index_sequence<sizeof...(Funcs)>());
    }
};

想法是將每個Funcs包裝在 lambda 中,以便僅調用與傳遞的索引相對應的 function。 注意|| 在折疊表達式短路。 會這樣使用:

float q0() { return 0.f; }
int q1() { return 1; }
std::string q2() { return "two"; }


int main() {

    bool success = FuncSwitch<q0, q1, q2>::Call(1);
}

有關完整示例,請參見此處

鑒於您“當前的嘗試”......在我看來,您幾乎可以編寫一個handle_cases結構/類,如下所示

struct handle_cases
 {
   std::map<int, std::function<void()>> m;

   template <typename ... F>
   handle_cases (std::pair<int, F> const & ... p)
      : m{ {p.first, [=]{ print_result(p.second); } } ... }
    { }

   void operator() (int i)
    { m[i](); }
 };

with a map between an integer and a lambda that call print_result with the function and an operator() that call the requested lambda, given the corresponding index.

您可以按如下方式創建 class 的 object (不幸的是,我看不到避免使用std::make_pair()的方法)

handle_cases hc{ std::make_pair(10, q1),
                 std::make_pair(20, q2),
                 std::make_pair(30, q3),
                 std::make_pair(40, q4) };

並按如下方式使用它

hc(30);

下面是一個完整的編譯示例

#include <functional>
#include <map>
#include <iostream>

template <typename T>
void print_result (T(*func)())
 {
   T res = func();
   std::cout << res << std::endl;
 }

struct handle_cases
 {
   std::map<int, std::function<void()>> m;

   template <typename ... F>
   handle_cases (std::pair<int, F> const & ... p)
      : m{ {p.first, [=]{ print_result(p.second); } } ... }
    { }

   void operator() (int i)
    { m[i](); }
 };

char      q1 () { return '1'; }
int       q2 () { return 2; }
long      q3 () { return 3l; }
long long q4 () { return 4ll; }

int main ()
 {
   handle_cases hc{ std::make_pair(10, q1),
                    std::make_pair(20, q2),
                    std::make_pair(30, q3),
                    std::make_pair(40, q4) };

   hc(30);
 }

暫無
暫無

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

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