Processing #ifdef, which were used to create several versions of the algorithm

I am trying to compare many (about 25) variations of an algorithm written in C ++.

I implemented these options using a combination of three methods:

  • copying code and making minor changes to the copied version

  • subclassing the base algorithm class

  • using #ifdefto switch between code snippets

The changes that arise from options 1 and 2 are fine, because I can choose which algorithm change will be performed in the configuration file. Then I can iterate through different configuration files and record the pairs “configuration: results” - saving these records is very important for my work.

I currently have a problem with #ifdef, because I have to compile several versions of the code to access these options, which makes it very difficult to run automated experiment scripts and save exact results. However, #ifdefs are very useful, because if I find an error in one copy of the code, then I should not forget to correct this error in several copies.

#ifdef Expand the six options that I created using both the copy code and the subclassification into 24 common variations (4 options for each base variation).

Here is an example - mostly I use #ifdefto avoid reusing too much code:

    ....

    double lasso_gam=*gamma;
    *lasso_idx=-1;
    for(int aj=0;aj<(int)a_idx.size();aj++){
        int j=a_idx[aj];
        assert(j<=C*L);
        double inc=wa[aj]*(*gamma)*signs[aj];
        if( (beta_sp(j)>0 && beta_sp(j)+inc<0)
#ifdef ALLOW_NEG_LARS
            || (beta_sp(j)<0 && beta_sp(j)+inc>0)
#else
            || (beta_sp(j)==0 && beta_sp(j)+inc<0)
#endif
            ){
            double tmp_gam=-beta_sp(j)/wa[aj]*signs[aj];

            if(tmp_gam>=0 && tmp_gam<lasso_gam) {
                *lasso_idx=aj;
                *next_active=j;
                lasso_gam=tmp_gam;
            }
        }
    }

    if(lasso_idx>=0){
        *gamma=lasso_gam;
    }

    ....

:. , #ifdef s, , , .

.

+5
6

#if , #if if , , , . , , , , , , . promises.

#if, , . , , , #if , 50 .

+1

(, ) :

enum class algorithm_type
{
    type_a,
    type_b,
    type_c
};

template <algorithm_type AlgorithmType>
void foo(int usual, double args)
{
    std::cout << "common code" << std::endl;

    if (AlgorithmType == algorithm_type::type_a)
    {
        std::cout << "doing type a..." << usual << ", " << args << std::endl;
    }
    else if (AlgorithmType == algorithm_type::type_b)
    {
        std::cout << "doing type b..." << usual << ", " << args << std::endl;
    }
    else if (AlgorithmType == algorithm_type::type_c)
    {
        std::cout << "doing type c..." << usual << ", " << args << std::endl;
    }

    std::cout << "more common code" << std::endl;
}

:

foo<algorithm_type::type_a>(11, 0.1605);
foo<algorithm_type::type_b>(11, 0.1605);
foo<algorithm_type::type_c>(11, 0.1605);

, , ( , , ). , ( , ).

:

#include <stdexcept>

void foo_with_runtime_switch(algorithm_type algorithmType,
                             int usual, double args)
{
    switch (algorithmType)
    {
    case algorithm_type::type_a:
        return foo<algorithm_type::type_a>(usual, args);
    case algorithm_type::type_b:
        return foo<algorithm_type::type_b>(usual, args);
    case algorithm_type::type_c:
        return foo<algorithm_type::type_c>(usual, args);
    default:
        throw std::runtime_error("wat");
    }
}

foo_with_runtime_switch(algorithm_type::type_a, 11, 0.1605);
foo_with_runtime_switch(algorithm_type::type_b, 11, 0.1605);
foo_with_runtime_switch(algorithm_type::type_c, 11, 0.1605);

( , ), , , . ( , enum, , , , .)

, , - #define :

#define FOO_ALGORITHM algorithm_type::type_a

void foo_with_define(int usual, double args)
{
    return foo<FOO_ALGORITHM>(usual, args);
}

foo_with_define(11, 0.1605);

, .

: , , , , , , ( #define):

// foo.hpp

enum class algorithm_type
{
    type_a,
    type_b,
    type_c
};

// for those who know which algorithm to use
template <algorithm_type AlgorithmType>
void foo(int usual, double args)
{
    std::cout << "common code" << std::endl;

    if (AlgorithmType == algorithm_type::type_a)
    {
        std::cout << "doing type a..." << usual << ", " << args << std::endl;
    }
    else if (AlgorithmType == algorithm_type::type_b)
    {
        std::cout << "doing type b..." << usual << ", " << args << std::endl;
    }
    else if (AlgorithmType == algorithm_type::type_c)
    {
        std::cout << "doing type c..." << usual << ", " << args << std::endl;
    }

    std::cout << "more common code" << std::endl;
}

// for those who will know at runtime
void foo(algorithm_type algorithmType, int usual, double args)
{
    switch (algorithmType)
    {
    case algorithm_type::type_a:
        return foo<algorithm_type::type_a>(usual, args);
    case algorithm_type::type_b:
        return foo<algorithm_type::type_b>(usual, args);
    case algorithm_type::type_c:
        return foo<algorithm_type::type_c>(usual, args);
    default:
        throw std::runtime_error("wat");
    }
}

#ifndef FOO_ALGORITHM
    // chosen to be the best default by profiling
    #define FOO_ALGORITHM algorithm_type::type_b
#endif

// for those who just want a good default
void foo(int usual, double args)
{
    return foo<FOO_ALGORITHM>(usual, args);
}

, , . , , .

+4

#ifdef s, script, . Makefile :

%-FOO.o: %.cc
        $(CXX) -c $(CFLAGS) -DFOO -o $@ $<

%-BAR.o: %.cc
        $(CXX) -c $(CFLAGS) -DBAR -o $@ $<

test-FOO: $(SRCS:%.cc=%-FOO.o)
        $(CXX) $(LDFLAGS) -DFOO -o $@ $^ $(LDLIBS)
+4

, .

class foo {
public:
  void do_something() {
    std::cout << "foo!" << std::endl;
  }
}

class bar {
public:
  void do_something() {
    std::cout << "bar!" << std::endl;
}

template <class meh>
void something() {
  meh algorithm;
  meh.do_something();
}

int main() {
  std::vector<std::string> config_values = get_config_values_from_somewhere();
  for (const austo& config : config_values) { // c++11 for short notation
    switch (config) {
      case "foo":
        something<foo>();
        break;
      case "bar":
        something<bar>();
        break;
      default:
        std::cout << "undefined behaviour" << std::endl;
    }
  }
}

, . , , ( ).

factory ( ), /, .

: .

0

, , #defines . gcc , , -D MYTESTFOO, MYTESTFOO. #defines - , , , , .

0

: :

#define METHOD METHOD1
int Method1() { return whatever(); };
#undef METHOD

#define METHOD METHOD2
int Method2() { return whatever(); };
#undef METHOD

Assuming that it whateverdepends on METHOD, then they will give different results.

0
source

All Articles