Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ways to generalize C++ code with different similar types

Currently I have the following code:

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);
    }
}

which has basically the same logic, but differents in types group_info.public_pools[i] and group_info.private_pool[i], that's why in second loop we have to add .pool_id member call. These types are different and don't have any relationships.

I want to rewrite this code to make it more general, for example something like this (sketch):

// 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);
}

But the problem is in different types for public_pools and private_pool.

Question: Could you give all ways how is possible to generalize this code with examples? I need C++03 code, but C++11/C++14 is acceptable.

My thoughts:

  1. Instead of SDK::revokeIPPoolFromNetworkInterface make wrapperIPPool with overloads for both types and call SDK::revokeIPPoolFromNetworkInterface internally.
  2. Overload revokeIPPool for both types (but it's code duplication, no improvement against original code)
  3. Partial function template specialization for revokeIPPool (is this possible?)
  4. Full function template specialization for revokeIPPool
  5. Wrap function revokeIPPool in class and make partial class template specialization.

Questions:

  1. Wich of my thoughts are correct?
  2. What's about advantages and shortcomings?
  3. Which is more idiomatic for C++03 or C++11?
  4. Is there other solutions?
like image 398
likern Avatar asked Mar 18 '23 17:03

likern


2 Answers

In C++03 I'd use template specialization on a traits class. (The main reason for going through a traits class, rather than using direct function specialisation, or overload is that you can perform partial template specialisation for classes, but not functions - and while its not needed here, it may be something that is useful later )

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; }
}
like image 174
Michael Anderson Avatar answered Apr 01 '23 20:04

Michael Anderson


Dunno if generic lambdas (C++14) are an acceptable solution for you:

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);
};

Then you only hace to define a lambda to access the pool item.

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; });
like image 30
codestation Avatar answered Apr 01 '23 20:04

codestation