【问题标题】:Ways to generalize C++ code with different similar types泛化具有不同相似类型的 C++ 代码的方法
【发布时间】:2014-12-05 20:45:09
【问题描述】:

目前我有以下代码:

static void markPoolsFree(const TNetgroupPools &group_info, TObjectID netiface) {
    for (size_t i = 0; i<group_info.public_pools.length();i ++ ) {
        SDK::revokeIPPoolFromNetworkInterface(group_info.public_pools[i],netiface);
    }

    for (size_t i =0 ; i<group_info.private_pool.length(); i++) {
        SDK::revokeIPPoolFromNetworkInterface(group_info.private_pool[i].pool_id,
                                              netiface);
    }
}

其逻辑基本相同,但group_info.public_pools[i]group_info.private_pool[i] 类型不同,这就是为什么在第二个循环中我们必须添加.pool_id 成员调用。这些类型是不同的,没有任何关系。

我想重写这段代码以使其更通用,例如像这样(草图):

// template function
template <typename Container, typename PredArgs>
static void revokeIPPool(const Container &pool, TObjectID netiface,
                         bool (*pred)(PredArgs pool_id, PredArgs netiface_id))
{
     for (size_t i = 0; i < pool.length(); ++i) {
         if (pred(pool[i], netiface)) {
             SDK::revokeIPPoolFromNetworkInterface(pool[i], netiface);
         }

     }
}

// calling code
static void markPoolsFree(const TNetgroupPools &group_info, TObjectID netiface) {
    revokeIPPool(group_info.public_pools, netiface, SDK::isPredicateTrue);
    revokeIPPool(group_info.private_pool, netiface, SDK::isPredicateTrue);
}

但问题在于public_poolsprivate_pool 的类型不同。

问题: 您能否提供所有方法,如何通过示例概括此代码?我需要 C++03 代码,但是 C++11/C++14 是可以接受的。

我的想法:

  1. 代替SDK::revokeIPPoolFromNetworkInterface 使wrapperIPPool 具有两种类型的重载并在内部调用SDK::revokeIPPoolFromNetworkInterface
  2. 两种类型都重载revokeIPPool(但它是代码重复,对原始代码没有改进)
  3. revokeIPPool 的部分函数模板特化(这可能吗?)
  4. revokeIPPool 的全功能模板特化
  5. 在类中包装函数revokeIPPool 并使部分类模板特化。

问题:

  1. 我的哪些想法是正确的?
  2. 优缺点是什么?
  3. 对于 C++03 或 C++11,哪个更惯用?
  4. 还有其他解决方案吗?

【问题讨论】:

  • 私有池的.pool_idpublic_pool的类型相同。重载一个函数,该函数在公共池上返回输入,在私有池上返回 .pool_id。这可以让你成功。

标签: c++ templates c++11 c++14


【解决方案1】:

在 C++03 中,我会在特征类上使用模板特化。 (通过特性类而不是使用直接函数特化或重载的主要原因是您可以对类执行部分模板特化,但不能对函数执行部分模板特化——虽然这里不需要,但以后可能会有用)

static void markPoolsFree(const TNetgroupPools &group_info, TObjectID netiface) {
    markPoolsFreeImpl(group_info.public_pools, netiface);
    markPoolsFreeImpl(group_info.private_pools, netiface);
}

template<typename T>
static void markPoolsFreeImpl(POOLS pools, TObjectID netiface) {
    for (size_t i = 0; i<pools.length();i ++ ) {
       PoolId poolid = PoolListTrait<POOLS>::get(pools,i);
       SDK::revokeIPPoolFromNetworkInterface(poolid,netiface);
    }
}

template<typename T> class PoolListTrait {};

template<> class PoolListTrait<PublicPoolList> {
  static PoolId get(PublicPoolList pools, int i) { return pools[i]; }
}

template<> class PoolListTrait<PrivatePoolList> {
  static PoolId get(PrivatePoolList pools, int i) { return pools[i].pool_id; }
}

【讨论】:

    【解决方案2】:

    不知道通用 lambdas (C++14) 是否适合您:

    auto revoke_pool = [](auto &pool, TObjectID netiface, auto extract_item)
    {
        for(std::size_t i = 0; i < pool.length(); ++i)
            SDK::revokeIPPoolFromNetworkInterface(extract_item(pool, i), netiface);
    };
    

    然后你只需要定义一个 lambda 来访问池项。

    revoke_pool(group_info.public_pools, netiface, [](auto &pool, std::size_t idx) { return pool[idx]; });
    revoke_pool(group_info.private_pool, netiface, [](auto &pool, std::size_t idx) { return pool[idx].pool_id; });
    

    【讨论】:

      【解决方案3】:

      随着我们从 C++03 到 C++11 再到 C++14,我们的解决方案在减少总代码和能够更加本地化方面都变得更短 - 两者都是明显的胜利。在 C++14 中,我们只需一个 10 行函数即可完成,而在 C++03 中,我们需要 4 个函数。以下是我在三个功能集中的操作方式:


      C++03:函子!基本上,您应该如何使用当天的每个标准算法(或免费功能)。

      template <typename POOL, typename EXTRACTOR>
      static void markPoolsFreeImpl(const POOL &pool, TObjectID netiface, 
          EXTRACTOR ex)
      {
          for (std::size_t i = 0; i < pool.length(); ++i) {
              SDK::removeIPPoolFromNetworkInterface(ex(pool[i]), netiface);
          }
      }
      
      
      static void markPoolsFree(const TNetgroupPools &group_info, TObjectID netiface) {
          markPoolsFreeImpl(group_info.public_pools,
                        netiface,
                        PublicExtractor());
      
          markPoolsFreeImpl(group_info.private_pools,
                        netiface,
                        PrivateExtractor());
      }
      
      // fill in T, U, V as appropriate here, I dunno what they are
      struct PublicExtractor {
          T operator()(const U& item) const { return item; }
      };
      
      struct PrivateExtractor {
          T operator()(const V& item) const { return item.pool_id; }
      };
      

      这也可能看起来像:

      T PublicExtractor(const U& item) { return item; }
      

      只需将其传入,无需额外的()s。如果我们不小心为markPoolsFreeImplEXTRACTOR 提供了错误的operator(),它就不会编译指向调用它的行。这不是很好,但它与我们能做的一样好。


      C++11:LambdasmarkPoolsFreeImpl 函数看起来是一样的,只是不是在任何地方定义我们的函子,我们可以内联定义它们:

      static void markPoolsFree(const TNetgroupPools &group_info, TObjectID netiface) {
          markPoolsFreeImpl(group_info.public_pools,
                        netiface,
                        [](const U& item) { return item; });
      
          markPoolsFreeImpl(group_info.private_pools,
                        netiface,
                        [](const V& item) { return item.pool_id; });
      }
      

      此外,如果我们传递了错误类型的提取器函数,我们可以使错误消息更清晰(再次,酌情填写T):

      template <typename POOL, typename EXTRACTOR>
      static void markPoolsFreeImpl(const POOL &pool, TObjectID netiface, 
          EXTRACTOR ex)
      {
          static_assert(std::is_same<T, decltype(ex(pool[0]))>::value, 
                        "invalid EXTRACTOR: must return a T");
      
          for (std::size_t i = 0; i < pool.length(); ++i) {
              SDK::removeIPPoolFromNetworkInterface(ex(pool[i]), netiface);
          }
      }
      

      C++14:更简单的 Lambdas!我们甚至不必识别UV

      static void markPoolsFree(const TNetgroupPools &group_info, TObjectID netiface) {
          markPoolsFreeImpl(group_info.public_pools,
                        netiface,
                        [](const auto& item) { return item; });
      
          markPoolsFreeImpl(group_info.private_pools,
                        netiface,
                        [](const auto& item) { return item.pool_id; });
      }
      

      甚至可以将markPoolsFreeImpl 变成本地 lambda:

      static void markPoolsFree(const TNetgroupPools &group_info, TObjectID netiface) {
          // netiface is captured
          auto impl = [&](const auto& pool, auto ex) {
              static_assert(std::is_same<T, decltype(ex(pool[0]))>::value, "");
      
              for (std::size_t i = 0; i < pool.length(); ++i) {
                  SDK::removeIPPoolFromNetworkInterface(ex(pool[i]), netiface);
              }
          });
      
          impl(group_info.public_pools, [](const auto& item) { return item; });
          impl(group_info.private_pools, [](const auto& item) { return item.pool_id; });
      }
      

      【讨论】:

        猜你喜欢
        • 2014-09-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-16
        • 1970-01-01
        • 2020-11-26
        • 1970-01-01
        相关资源
        最近更新 更多