2

I have a config file in my application like below. This config need to be done before build the program.

#define NO_OF_COLORS 8   

#define COLOR_RESPONCE_1 'R'
#define COLOR_RESPONCE_2 'G'
#define COLOR_RESPONCE_3 'B'
#define COLOR_RESPONCE_4 'M'
#define COLOR_RESPONCE_5 'C'
#define COLOR_RESPONCE_6 'Y'
#define COLOR_RESPONCE_7 'O'
#define COLOR_RESPONCE_8 'P'

Inside a class I need to add these set of values to a std::vector. Is there a way to iterate through these defines. May be using a macro?

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
Nayana Adassuriya
  • 23,596
  • 30
  • 104
  • 147
  • i don't think you can iterate through define as it is permanent and cannot be tampered with – Francis Fuerte Oct 22 '14 at 05:55
  • 2
    Please could somebody tell me why certain programmers like macros – Ed Heal Oct 22 '14 at 05:56
  • Also you can convert between RGB and CMYK – Ed Heal Oct 22 '14 at 05:59
  • @ Ed Heal, `char`s are expected user inputs for a console applications. filled vector uses to validate the inputs – Nayana Adassuriya Oct 22 '14 at 06:01
  • 1
    English would be nice – Ed Heal Oct 22 '14 at 06:17
  • Have you seen [this aproach](http://codereview.stackexchange.com/a/63249/50647)? I think it will help you to change the config without the need to build the application again. Creating a class of config, will also let you get any value you want. More, you can also return a vector and iterate it. – sop Oct 22 '14 at 07:17

5 Answers5

2

You can use the Boost.Preprocessor library. It provides macros that simulate control constructs and data structures for the prepropcessor. E.g. you can iterate over your colors like this:

#include <iostream>
#include <boost/preprocessor.hpp>

#define NO_OF_COLORS 8

#define COLOR_RESPONCE_1 'R'
#define COLOR_RESPONCE_2 'G'
#define COLOR_RESPONCE_3 'B'
#define COLOR_RESPONCE_4 'M'
#define COLOR_RESPONCE_5 'C'
#define COLOR_RESPONCE_6 'Y'
#define COLOR_RESPONCE_7 'O'
#define COLOR_RESPONCE_8 'P'

#define PRINT_COLOR(z, x, _)                    \
  std::cout << BOOST_PP_CAT(COLOR_RESPONCE_, x) << std::endl;

int main()
{
  BOOST_PP_REPEAT_FROM_TO(1, NO_OF_COLORS, PRINT_COLOR, 0);
}

BOOST_PP_REPEAT_FROM_TO simulates loop with counter, PRINT_COLOR macro implements the loop body.

Personally I would use Boost.Preprocessor only if I have no other choice. It only simulates control constructs and have a number of limitations (e.g. on a number of loop iterations), also debugging preprocessor code is no simpler than heavy template code.

A note on style. Although preprocessor is inherited from C and is necessary for separate (and also conditional) compilation, it is alien to modern C++, in fact is alien even for pre-98 C++. If you need constants you can and should define them with const, enum (or even better with enum class) or constexpr. These constants can be put in namespaces to avoid name clashes, they can be guaranteed to be initialized only once (extern const).

Begemoth
  • 1,389
  • 9
  • 14
1

There is no way you can iterator through macros. Pre-processor compilation may be multi-phase, but the names itself would be resolved in first pass (of preprocessing) only.

Better if you do this way:

const char Colors[] = {'R','G','B','C','M','Y','O','P'};

And then use sizeof(Colors) / sizeof(Colors[0]) to find number of elements in this array. Iterate using a loop:

for(size_t index = 0; index < sizeof(Colors) / sizeof(Colors[0]); ++index)
{}

Or, if you are using C++11 compiler, which support range-based for loops, you may use:

for(char color : Colors) 
{ 
   // Use 'color'
}

More simply:

for(auto color : Colors) 
{ 
   // Use 'color'
}
RamblingMad
  • 5,332
  • 2
  • 24
  • 48
Ajay
  • 18,086
  • 12
  • 59
  • 105
1

Why not just store these in a std::string rather than a std::vector?

const std::string COLDATA = "RGBMCYOP";

You still get to access it like a std::vector using indexing []:

COLDATA[3]; // == 'M'

Or if you still want to put that in a vector you can do so like this:

const std::vector<char> COLDATAVEC(COLDATA.begin(), COLDATA.end());

If you are using C++11 you can put it into a std::vector without using a std::string:

const std::vector<char> COLDATAVEC = {'R', 'G', 'B', 'M', 'C', 'Y', 'O', 'P'};
Galik
  • 47,303
  • 4
  • 80
  • 117
1

Define your own iterating machinery (limited to 10 iterations):

#define ITER_0(MACRO, ACTION, DATA)
#define ITER_1(MACRO, ACTION, DATA) ITER_0(MACRO, ACTION, DATA) ACTION(MACRO##1, DATA)
#define ITER_2(MACRO, ACTION, DATA) ITER_1(MACRO, ACTION, DATA) ACTION(MACRO##2, DATA)
#define ITER_3(MACRO, ACTION, DATA) ITER_2(MACRO, ACTION, DATA) ACTION(MACRO##3, DATA)
#define ITER_4(MACRO, ACTION, DATA) ITER_3(MACRO, ACTION, DATA) ACTION(MACRO##4, DATA)
#define ITER_5(MACRO, ACTION, DATA) ITER_4(MACRO, ACTION, DATA) ACTION(MACRO##5, DATA)
#define ITER_6(MACRO, ACTION, DATA) ITER_5(MACRO, ACTION, DATA) ACTION(MACRO##6, DATA)
#define ITER_7(MACRO, ACTION, DATA) ITER_6(MACRO, ACTION, DATA) ACTION(MACRO##7, DATA)
#define ITER_8(MACRO, ACTION, DATA) ITER_7(MACRO, ACTION, DATA) ACTION(MACRO##8, DATA)
#define ITER_9(MACRO, ACTION, DATA) ITER_8(MACRO, ACTION, DATA) ACTION(MACRO##9, DATA)
#define ITER_10(MACRO, ACTION, DATA) ITER_9(MACRO, ACTION, DATA) ACTION(MACRO##10, DATA)

#define ITER_I(ITERS, MACRO, ACTION, DATA) ITER_##ITERS(MACRO, ACTION, DATA)
#define ITER(ITERS, MACRO, ACTION, DATA) ITER_I(ITERS, MACRO, ACTION, DATA)

Define actions that will get invoked on each iteration:

#define PRINT(MACRO, DATA) std::cout << MACRO; 
#define ADD_TO_VECTOR(MACRO, DATA) DATA.push_back(MACRO);

Use those actions like below:

std::vector<char> v;
ITER(NO_OF_COLORS, COLOR_RESPONCE_, ADD_TO_VECTOR, v)

ITER(NO_OF_COLORS, COLOR_RESPONCE_, PRINT, ~)

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
0

It seems you want to iterate over an enumeration. C++ does not support this (other languages, such as Java do have this behavior by default. To work around this, you want to create a data structure that is iterable, constant in length, and well defined, so it is easy to maintain.

For, example you can store the data in an std::array (or before c++11 std::tr1::array)

const std::array<BYTE,8> Colors = { { 'R','G','B','C','M','Y','O','P' } };

The std::array has iterators, and a size() operator. So you can do

for( std::array<BYTE,8>::const_iterator itr = Colors.begin();
     itr != Colors.end();
     ++itr )
{
    // use (*itr)
}

or if you want the index

for( std::size_t index = 0 ; index < Colors.size() ; ++index )
{
    // use Colors[i]
}

and in c++11 you can use:

for( auto color : Colors )
{
     // use color     
} 

You can also copy them to an std::vector by:

std::vector<BYTE> Colors_vector( Colors.begin(), Colors.end() );
tillaert
  • 1,797
  • 12
  • 20