简体   繁体   中英

Turning #ifdef's into a template metaprogram in C++

I have the following code inside a C++ class:

class Features
{
    #define Feature_Size_A 12345
    #define Feature_Size_B 45678
    #define Feature_Size_C 78901
    //#define Feature_Size_D 14725

    const int Feature_Sum = 0
    #ifdef Feature_Size_A
        + Feature_Size_A
    #endif
    #ifdef Feature_Size_B
        + Feature_Size_B
    #endif
    #ifdef Feature_Size_C
        + Feature_Size_C
    #endif
    #ifdef Feature_Size_D
        + Feature_Size_D
    #endif
        ;

    #ifdef Feature_Size_A
        static float Feature_A[Feature_Size_A];
    #endif
    #ifdef Feature_Size_B
        static float Feature_B[Feature_Size_B];
    #endif
    #ifdef Feature_Size_C
        static float Feature_C[Feature_Size_C];
    #endif
    #ifdef Feature_Size_D
        static float Feature_D[Feature_Size_D];
    #endif
};

I used to comment out features, like line 4, to compile and run different tests. But now I'd like to have the class as a template, so I can instantiate several versions with different features turned on or off in the same program.

I'm thinking of something like this:

template <bool Feature_A, bool Feature_B, bool Feature_C, bool Feature_D>
class Features
{
    ...
};

Features<true, true, true, false> f;

I tried with boost::mpl:vector's but I'm struggling harshly.

BTW: This is not the complete code. The original code has 25 features.

I'm thankful for every idea not involving macros :-)

Type lists can be used to solve this problem.

template<unsigned num, unsigned size, typename T>
class Feature : public T
{
public:
    static float feature[size];
    static const unsigned int feature_sum = size + T::feature_sum;
};
template<unsigned num, unsigned size, typename T>
float Feature<num, size, T>::feature[size];
class Tail { 
public:
    static const unsigned feature_sum = 0; 
};

template<unsigned num, unsigned size, typename T>
float* get_feature_arr(Feature<num, size, T>& ref) 
{
    return ref.feature;
}

int main() {
    Feature<1, 12345, Feature<2, 45678, Feature<4, 78901, Tail>>> TripleFeatures;
    auto first = get_feature_arr<1>(TripleFeatures);
    auto third = get_feature_arr<4>(TripleFeatures);
    auto size = TripleFeatures.feature_sum;
}

This can also be used to access any feature, regardless of what other features are or aren't in the list.

Edit: I nubbed up some of the details, like not defining the array and trying to have "3features" as an identifier. Le fixed. Code compiles GCC 4.7.1.

Why not use statically allocated arrays?

#include <stdio.h>

template <bool Feature_A, bool Feature_B, bool Feature_C, bool Feature_D>
class Features
{
    static const int Feature_Size_A = 12345;
    static const int Feature_Size_B = 45678;
    static const int Feature_Size_C = 78901;
    static const int Feature_Size_D = 14725;
    static const int Feature_Sum = 0
        + Feature_A ? Feature_Size_A : 0
        + Feature_B ? Feature_Size_B : 0
        + Feature_C ? Feature_Size_C : 0
        + Feature_D ? Feature_Size_D : 0
    ;

public:
    static float Feature_Vector_A[Feature_A ? Feature_Size_A : 0];
    static float Feature_Vector_B[Feature_B ? Feature_Size_B : 0];
    static float Feature_Vector_C[Feature_C ? Feature_Size_C : 0];
    static float Feature_Vector_D[Feature_D ? Feature_Size_D : 0];
};

Features<true, true, true, true> f1;
Features<true, true, true, false> f2;

int main()
{
    printf("%d %d\n", sizeof(f1.Feature_Vector_D), sizeof(f2.Feature_Vector_D));
}

Output:

58900 0

It is not clear exactly what the features will be exactly, but here is a solution that allows you to conditionnaly include member functions as well as member data:

namespace mpl = boost::mpl;

// Define your features
struct FeatureA
{
    static const int size = 12345;
    static float Feature_A[size];

    static void methodA() {}
};
float FeatureA::Feature_A[12345];

struct FeatureB
{
    static const int size = 45678;
    static char Feature_B[size]; // possibly different types of data (?)

    static void methodB() {}
};
float FeatureB::Feature_B[45678];

struct FeatureC
{
    static const int size = 78901;
    static int Feature_C[size];

    static void methodC() {}
};
float FeatureC::Feature_C[78901];


// Helper metafunction
template <typename T>
struct get_size 
  : mpl::int_<T::size>
{};


template <typename FeaturesSeq>
struct Features_impl 
  : mpl::inherit_linearly<
        FeaturesSeq,
        mpl::inherit<mpl::_, mpl::_>
    >::type
{
    static const int Feature_Sum = 
        mpl::accumulate<
            FeaturesSeq,
            mpl::int_<0>,
            mpl::plus<
                mpl::_1,
                get_size<mpl::_2>
            >
        >::type::value;
};

template <typename... F>
using Features = Features_impl<mpl::vector<F...>>;


#include <iostream>

int main()
{
    typedef Features<FeatureA, FeatureC> F;

    std::cout << F::Feature_Sum << '\n';

    F::Feature_A[0] = 12.0f;
    F::methodA();

    F::methodC();
}

If all your features are really just float arrays, as in your example, you can use a generic Feature class

template <int Size>
struct Feature
{
    static float data[Size];
};
template <int Size>
float Feature::data[Size];

And store specializations of this class into mpl vectors:

typedef mpl::vector<Feature<1234>, Feature<5678>> Features;

mpl::at_c<Features, 0>::type::data[0] = 12.0f;
// could be encapsulated into a helper function

Without more information about the purpose of these so-called features, it is hard to provide a more complete answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM