Boost.Fusion运行时开关 [英] Boost.Fusion run-time switch

查看:204
本文介绍了Boost.Fusion运行时开关的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从文件读取对象的类型:

I am reading the type of an object from a file:

enum class type_index { ... };
type_index typeidx = read(file_handle, type_index{});

根据类型索引,我想创建一个类型,并使用它做一些通用的(每个类型相同的通用代码):

Depending on the type index, I want to create a type (out of a list of possible types), and do something generic with it (the same generic code for each type):

std::tuple<type1, type2, ..., typeN> possible_types;

boost::fusion::for_each(possible_types, [&](auto i) {
  if (i::typeidx != typeidx) { return; }
  // do generic stuff with i
});

即:


  • 我对不同类型使用相同的通用代码

  • 我希望编译器为每种类型生成特定代码,

  • 知道我在运行时需要哪种类型,

  • 我只想为该类型的代码执行代码。

  • I have the same generic code for different types,
  • I want the compiler to generate specific code for each type,
  • I only know which type I need at runtime, and
  • I want to execute the code for that single type only.

这感觉像一个带有运行时条件的开关语句,但是其中cases是在编译时生成的。特别是,这不觉得像一个 for_each 语句(我不是对矢量,元组,列表中的所有元素做任何事情,但只对一个元素) 。

This feels like a switch statement with a run-time condition, but where the "cases" are generated at compile-time. In particular, this does not feel like a for_each statement at all (I am not doing anything for all elements in a vector, tuple, list, but only to a single element).

有没有更清楚的方式来表达/写这个成语? (例如对于可能的类型使用 mpl :: vector 而不是 std :: tuple for_each 算法,...)

Is there a better clearer way to express/write this idiom? (E.g. use an mpl::vector instead of a std::tuple for the possible types, use something different than the for_each algorithm,...)

推荐答案

继承的lambdas伎俩:

I like my usual inherited lambdas trick:

我在

  • Lambda functions as base classes
  • what is the correct way to handle multiple input command differently in c++? (where it visits members of a boost::variant)

我相信我已经看到Sumant Tambe在他最近的 cpptruths.com 帖子中使用它。

I believe I've seen Sumant Tambe use it in his more recent cpptruths.com postings.

现在是一个演示。

最重要的技巧是使用 boost :: variant 隐藏类型代码denum为我们。

The most important trick applied is that I use boost::variant to hide the type code denum for us. But the principle applies even if you keep your own type discrimination logic (just requiring more coding)

Live On Coliru

#include <boost/serialization/variant.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <fstream>
#include <iostream>

using namespace boost; // brevity

//////////////////
// This is the utility part that I had created in earlier answers:
namespace util {
    template<typename T, class...Fs> struct visitor_t;

    template<typename T, class F1, class...Fs>
    struct visitor_t<T, F1, Fs...> : F1, visitor_t<T, Fs...>::type {
        typedef visitor_t type;
        visitor_t(F1 head, Fs...tail) : F1(head), visitor_t<T, Fs...>::type(tail...) {}

        using F1::operator();
        using visitor_t<T, Fs...>::type::operator();
    };

    template<typename T, class F> struct visitor_t<T, F> : F, boost::static_visitor<T> {
        typedef visitor_t type;
        visitor_t(F f) : F(f) {}
        using F::operator();
    };

    template<typename T=void, class...Fs>
    typename visitor_t<T, Fs...>::type make_visitor(Fs...x) { return {x...}; }
}

using util::make_visitor;

namespace my_types {
    //////////////////
    // fake types for demo only
    struct A1 {
        std::string data;
    };

    struct A2 {
        double data;
    };

    struct A3 {
        std::vector<int> data;
    };

    // some operations defined on A1,A2...
    template <typename A> static inline void serialize(A& ar, A1& a, unsigned) { ar & a.data; } // using boost serialization for brevity
    template <typename A> static inline void serialize(A& ar, A2& a, unsigned) { ar & a.data; } // using boost serialization for brevity
    template <typename A> static inline void serialize(A& ar, A3& a, unsigned) { ar & a.data; } // using boost serialization for brevity

    static inline void display(std::ostream& os, A3 const& a3) { os << "display A3: " << a3.data.size() << " elements\n"; }
    template <typename T> static inline void display(std::ostream& os, T const& an) { os << "display A1 or A2: " << an.data << "\n"; }

    //////////////////
    // our variant logic
    using AnyA = variant<A1,A2,A3>;

    //////////////////
    // test data setup
    AnyA generate() { // generate a random A1,A2...
        switch (rand()%3) {
            case 0: return A1{ "data is a string here" };
            case 1: return A2{ 42 };
            case 2: return A3{ { 1,2,3,4,5,6,7,8,9,10 } };
            default: throw std::invalid_argument("rand");
        }
    }

}

using my_types::AnyA;

void write_archive(std::string const& fname) // write a test archive of 10 random AnyA
{
    std::vector<AnyA> As;
    std::generate_n(back_inserter(As), 10, my_types::generate);

    std::ofstream ofs(fname, std::ios::binary);
    archive::text_oarchive oa(ofs);

    oa << As;
}

//////////////////
// logic under test
template <typename F>
void process_archive(std::string const& fname, F process) // reads a archive of AnyA and calls the processing function on it
{
    std::ifstream ifs(fname, std::ios::binary);
    archive::text_iarchive ia(ifs);

    std::vector<AnyA> As;
    ia >> As;

    for(auto& a : As)
        apply_visitor(process, a);
}

int main() {
    srand(time(0));

    write_archive("archive.txt");

    // the following is c++11/c++1y lambda shorthand for entirely compiletime
    // generated code for the specific type(s) received
    auto visitor = make_visitor(
        [](my_types::A2& a3) { 
                std::cout << "Skipping A2 items, just because we can\n";
                display(std::cout, a3);
            },
        [](auto& other) { 
                std::cout << "Processing (other)\n";
                display(std::cout, other);
            }
        );

    process_archive("archive.txt", visitor);
}

列印

Processing (other)
display A3: 10 elements
Skipping A2 items, just because we can
display A1 or A2: 42
Processing (other)
display A1 or A2: data is a string here
Processing (other)
display A3: 10 elements
Processing (other)
display A1 or A2: data is a string here
Processing (other)
display A1 or A2: data is a string here
Processing (other)
display A3: 10 elements
Processing (other)
display A1 or A2: data is a string here
Processing (other)
display A3: 10 elements
Processing (other)
display A3: 10 elements

这篇关于Boost.Fusion运行时开关的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆