2012-07-16 67 views
2

在大多數通用術語中,我的問題如下:在編譯時定義一系列異構函數指針(可能具有不同的arity),以後需要用任意方式迭代和調用在運行時訂購。迭代和調用元組內的異構函數

將自己約束到C++,最適合的容器,迭代和調用機制是什麼?

這個問題是由現實世界的情況所驅動的,我後來發現了一個更簡單的解決方案,它不涉及元組,而是更專門化的本質。

本來我試圖做這樣的事情:

//type variables Y... have to be convertible to parameters of every function from the tuple std::tuple<T...> in order for this to compile 
template<size_t n, typename... T, typename... Y> 
void callFunNth(std::tuple<T...> &tpl, size_t i, Y... args) { 
    if (i == n) 
     std::get<n>(tpl)(args...); 
    else 
     callFunNth<(n < sizeof...(T)-1? n+1 : 0)>(tpl, i, args...); 
} 
template<typename... T, typename... Y> 
void callFun(std::tuple<T...> &tpl, size_t i, Y... args) { 
    callFunNth<0>(tpl,i, args...); 
} 

int main() 
{ 
    using T1 = int; 
    namespace mpi = boost::mpi; 
    //Several instantiations of boost::mpi::reduce algorithm I am interested in 
    auto algs = make_tuple(boost::bind((void (*)(const mpi::communicator&, const T1*, T1, T1*, std::plus<T1>, int))mpi::reduce<T1, std::plus<T1>>, _1, _2, _3, _4, std::plus<T1>(), _5), 
          boost::bind((void (*)(const mpi::communicator&, const T1*, T1, T1*, mpi::minimum<T1>, int))mpi::reduce<T1, mpi::minimum<T1>>, _1, _2, _3, _4, mpi::minimum<T1>(), _5), 
          boost::bind((void (*)(const mpi::communicator&, const T1*, T1, T1*, std::minus<T1>, int))mpi::reduce<T1, std::minus<T1>>, _1, _2, _3, _4, std::minus<T1>(), _5) 
         ); 

    //Iterate through the tuple and call each algorithm 
    for(size_t i=0; i < std::tuple_size<decltype(algs)>::value;i++) 
     callFun(algs, i, /*actual arguments to each algorithm*/); 
} 

這種方法的問題在於,對於callFunNth在所有提供的參數編譯必須類型轉換爲內部的所有的函數的參數提供的元組嚴重限制了所述函數的異構性,迫使用戶使用std :: bind或boost :: bind來解決此問題。

當類型是相互轉換一個可以寫出如下:

template <typename T, typename U> 
void fja(T x, U y) { 
    std::cout << x << std::endl; 
} 
auto funs = std::make_tuple(fja<int,std::string>, fja<double,std::string>, fja<char,std::string>); 
callFun(funs, 2, 'a', "Char"); 
callFun(funs, 1, 2.45, "Decimal"); 
callFun(funs, 0, 1, "Integer"); 

和具有「一個」,「2.45」和送出分別到stdout

+0

你想遍歷元組(這將意味着元組內相當於參數的所有功能),或做你想基於一個運行時變量獲得一個特定的函數,並用特定的參數調用它? – Xeo 2012-07-16 14:44:06

+0

我不認爲這是可能調用'callFun(玩意兒,我,2.45,「十進制」);',因爲編譯器不知道的是,元組的「I」個元素可以接受雙,其中'我'直到運行時才知道。 – kennytm 2012-07-16 14:59:37

+0

@Xeo這是 – PetarMarendic 2012-07-16 15:08:16

回答

1

去到庫「1」異構容器是Boost.Fusion。正如您在該網站上看到的那樣,他們使用編譯時多態仿函數來執行此類任務。

2

您應該將函數對象存儲爲std::vector<std::function<const boost::mpi::communicator&, const T1*, int, T1*, int>>。管理起來要容易得多。

如果您必須使用函數元組,請參見下文。


C++標準庫嚴重需要編譯時iota

下面是另一種方法,如果你需要的是調用所有功能使用相同的參數。首先,我們建造一個可變整數列表integers<0, 1, 2, ..., n-1>(從https://github.com/kennytm/utils/blob/master/vtmp.hpp複製):

template <size_t... ns> 
struct integers 
{ 
    template <size_t n> 
    using push_back = integers<ns..., n>; 

}; 

namespace xx_impl 
{ 
    template <size_t n> 
    struct iota_impl 
    { 
     typedef typename iota_impl<n-1>::type::template push_back<n-1> type; 
    }; 

    template <> 
    struct iota_impl<0> 
    { 
     typedef integers<> type; 
    }; 
} 

template <size_t n> 
using iota = typename xx_impl::iota_impl<n>::type; 

然後我們使用直接拆包操作:

template <typename... T, size_t... ns, typename... Y> 
void call_all_impl(const std::tuple<T...>& funcs, 
        const integers<ns...>&, 
        Y... args) { 
    __attribute__((unused)) 
    auto f = {(std::get<ns>(funcs)(args...), 0)...}; 
} 

template <typename T, typename... Y> 
void call_all(const T& funcs, Y&&... args) { 
    call_all_impl(funcs, 
        iota<std::tuple_size<T>::value>(), 
        std::forward<Y>(args)...); 
} 

例如,

int main() { 
    call_all(std::make_tuple([](int x, double y){ printf("1: %d %g\n", x, y); }, 
          [](double x, int y){ printf("2: %e/%d\n", x, y); }, 
          [](int x, int y){ printf("3: %#x %#x\n", x, y); }), 
      4, 9); 
} 

打印

 
1: 4 9 
2: 4.000000e+00/9 
3: 0x4 0x9 

稍作修改可以使其僅調用在運行時選擇的第i個參數。

template <typename... T, size_t... ns, typename... Y> 
void call_one_impl(const std::tuple<T...>& funcs, size_t which, 
        const integers<ns...>&, 
        Y... args) { 
    __attribute__((unused)) 
    auto f = {(ns == which && (std::get<ns>(funcs)(args...), 0))...}; 
} 

template <typename T, typename... Y> 
void call_one(const T& funcs, size_t which, Y&&... args) { 
    call_one_impl(funcs, which, 
        iota<std::tuple_size<T>::value>(), 
        std::forward<Y>(args)...); 
} 

例如,

int main() { 
    auto t = std::make_tuple([](int x, double y){ printf("1: %d %g\n", x, y); }, 
          [](double x, int y){ printf("2: %e/%d\n", x, y); }, 
          [](int x, int y){ printf("3: %#x %#x\n", x, y); }); 

    call_one(t, 2, 6.5, 7.5); 
    call_one(t, 0, 4, 9); 
    call_one(t, 1, 5.8, 8); 
} 

打印

 
3: 0x6 0x7 
1: 4 9 
2: 5.800000e+00/8 
+1

編輯的措辭方式可能意味着OP要調用基​​於運行時可變特定的元組,這意味着簡單的迭代不會幫助。 :/ – Xeo 2012-07-16 14:57:13

+0

@Xeo:其實它可以提供幫助。查看更新。 (我認爲這是正確的,因爲鏗鏘++和g ++不抱怨'-pedantic -Wall -Wextra' ^^) – kennytm 2012-07-16 15:22:43

+0

@KennyTM:'auto f = {(ns == which &&(std :: get (funcs) (args ...),0))...}「哦!哦!這很聰明,很容易推廣到任意函數。你應該申請專利:更嚴重的是,仍然存在功能期望不同參數(可能是不同的矩陣)的問題。 – 2012-07-16 15:31:58

0

我認爲它是一個糟糕的主意,用元組用於存儲功能
對我來說,它更簡單地創建函數指針

typedef impl_defined pointer_to_function 

和推動功能於

std::vector<pointer_to_function> 
0

擺弄一點點這個問題之後,我相信使用型工會也許是解決它的一個相當不錯的辦法,但我不擔心這個問題,如規定它目前的形式有點太模糊,不值得一個最好的答案。

總之,這是一個可以如何使用型工會處理這個問題在C++:

int main() 
{ 
    auto f1 = [](int x) { std::cout << x << std::endl;}; 
    auto f2 = [](int x, std::string y) { std::cout << y << std::endl;}; 
    auto f3 = [](const std::vector<int> &x) { std::copy(x.begin(),x.end(),std::ostream_iterator<int>(std::cout," "));}; 
    using tFun1 = decltype(f1); 
    using tFun2 = decltype(f2); 
    using tFun3 = decltype(f3); 
    using funUnion = boost::variant<tFun1,tFun2,tFun3>; 

    std::vector<funUnion> funs = {f1,f2,f3}; 
    for(auto &ff : funs) { 
     if (tFun1* result = boost::get<tFun1>(&ff)) (*result)(2); 
     else 
     if (tFun2* result = boost::get<tFun2>(&ff)) (*result)(2,"Hello!"); 
     else 
     if (tFun3* result = boost::get<tFun3>(&ff)) (*result)({1,2,3,4,5}); 
    } 
}