首页 > 解决方案 > 如何防止手动实例化所有模板类型?

问题描述

我有一个函数Foo可以获取两个模板参数L3HdrTypeL4HdrType. 我收到一个数据包并解析它,然后需要调用该函数。我的代码目前看起来像:

ParsedPacket parsed_packet = parser.Parse(packet);
if (parsed_packet.ip_version() == IPv4 && parsed_packet.ip_proto() == TCP)
  Foo(parsed_packet.L3Header<ipv4_hdr>(), parsed_packet.L4Header<tcp_hdr>());
if (parsed_packet.ip_version() == IPv6 && parsed_packet.ip_proto() == TCP)
  Foo(parsed_packet.L3Header<ipv6_hdr>(), parsed_packet.L4Header<tcp_hdr>());
if (parsed_packet.ip_version() == IPv4 && parsed_packet.ip_proto() == UDP)
  Foo(parsed_packet.L3Header<ipv4_hdr>(), parsed_packet.L4Header<udp_hdr>());
if (parsed_packet.ip_version() == IPv6 && parsed_packet.ip_proto() == UDP)
  Foo(parsed_packet.L3Header<ipv6_hdr>(), parsed_packet.L4Header<udp_hdr>());

我的问题是,有没有办法减少这种代码重复?类似于以下内容:

Foo<parsed_packet.GetL3HeaderType(), parsed_packet.GetL4HeaderType()>(...);

这显然不起作用,因为在编译时不知道给定数据包的标头类型。

重复的来源是两个不同的 if 语句正在检查IPv4并将其映射到ipv4_hdr. 如果我可以在代码中的一个位置指定IPv4映射到ipv4_hdrthen 这将使代码随着选项的数量线性增长,而不是指数增长,因为我可以以某种方式编写:

if (parsed_packet.ip_version() == IPv4) {
  using L3HeaderType = ipv4_hdr;
}
...
Foo<L3HeaderType, L4HeaderType>(...)

请注意,我的实际代码需要支持的不仅仅是 4 种情况,因此实际上的代码比这里的示例难看得多,因为 if 语句的数量随着标头的数量呈指数增长。

标签: c++templates

解决方案


如果继承和运行时多态不是一个选项,您可以通过执行一些涉及 lambdas 的杂技来解耦两个模板参数的推导:

template <typename T> struct foo {};    
template <typename T> struct bar {};

// the function to be called
template <typename A, typename B>
void foobar( foo<A> f, bar<B> b) {}

// bind the first parameter
template <typename T>
auto make_foofoo (foo<T> f) {
    return [f](auto bar){ foobar(f,bar); };
}

// select second type here
template <typename F>
void do_the_actual_call(F f, int y) {
    if (y == 1) f(bar<int>{});
    if (y == 2) f(bar<double>{});
}


int main() {
    // the "conditions"
    int x = 1;
    int y = 2;

    // select first type here
    if (x == 1) {
        auto foofoo = make_foofoo(foo<int>{});
        do_the_actual_call(foofoo,y);
    } else if (x == 2){
        auto foofoo = make_foofoo(foo<double>{});
        do_the_actual_call(foofoo,y);
    }
}

它仍然是重复的代码,但它x + y不再像x * y.


推荐阅读