2

I got some constant int values I would like to use, So I used #define.

The issue is that I got some define names like this:

#define a1  123
#define a2  6543
#define a3  12
...

Is there a way in the code to somehow iterate over them to construct their name with strings and then get the define value of that constructed strings? A pseudo code of what I mean:

for(int i =0 ; i < 100; i++)
{
   string name = "a" + i;
   func(GetDefineName(name)); // should call here every time to func with a1,a2 etc.
}

I'm used to C# so I got some problems getting used to c++. Is define really the most appropriate way to do that?

CodeMonkey
  • 11,196
  • 30
  • 112
  • 203
  • Why wouldn't You use an array instead of defines? – bartop May 28 '18 at 06:55
  • The names themselves are also meaningful. the a1,a2 were just examples. In the real code they are more significate – CodeMonkey May 28 '18 at 06:56
  • Then maybe something like map. I can hardly imagine uses for preprocessor defines in modern C++ except some template magic. Map is something like C# dictionary FYI – bartop May 28 '18 at 07:00
  • 3
    Maybe use a `constexpr map` instead? It should be doable: https://stackoverflow.com/questions/26075969/compile-time-map-and-inverse-map-values?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa (and type safe, which `#define` are very much not so) – hauron May 28 '18 at 07:00
  • https://stackoverflow.com/a/8027208/1794345 seems like a good fit to solve your issue – Rerito May 28 '18 at 07:10
  • a map sounds like a good idea – CodeMonkey May 28 '18 at 07:14

5 Answers5

3

Lines starting with # are evaluated by the preprocessor - the compiler won't see them, only their effects. C++ code can therefore not interact with preprocessor variables, as they don't exist anymore when the code is compiled.

Constants can be defined that way, but C++ offers better approaches. For example they can be wrapped in namespaces to avoid name collisions.

//auto lets the compiler deduce the type for you
auto const my_var = 4.5; //double
auto const close_to_pi = 3.4f; //float
auto const the_answer = 42; //int
auto const kibibyte = 1024u; //unsigned int

To get more explanations about the type defining suffixes have a look at integer literals and floating point literals. You might also want to read up on constexpr later when you are more comfortable with C++ to explore compile time constant expressions.

Loebl
  • 1,381
  • 11
  • 21
2

Is there a way in the code to somehow iterate over them to construct their name with strings and then get the define value of that constructed strings?

No, there is no way to do that using defines.

You'll need to put the values into some container to be able to iterate the values.

If you just want to store the values, you can use a std::vector.

If you want to have both a name and a value, you can use std::map. Something like:

#include <iostream>
#include <string>
#include <map>

#define a0 42
#define a1 5
#define a2 15
#define a3 25

int main() {
    // Define the map
    std::map<std::string, int> m;

    // Intialize the map
    m["a0"] = a0;
    m["a1"] = a1;
    m["a2"] = a2;
    m["a3"] = a3;

    // Access map using name
    for(size_t i =0 ; i < m.size(); i++)
    {
        std::string name = "a" + std::to_string(i);
        std::cout << m[name] << std::endl;
    }

    // Iterate all map elements
    for (auto& x : m)
    {
        std::cout << x.first << ":" << x.second << std::endl;
    }
    return 0;
}
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
2

Another approach is to generate some C++ file containing what you want. So you'll need to set up your build for that purpose. You often use some build automation tool (such as GNU make, ninja, etc...) and you can configure it to generate some C++ file when needed (e.g. by adding a few lines in your Makefile, if you use make). The program generating some C++ code could be some script (e.g. in shell, awk, Python, Guile, ...), some specialized metaprogram (such as moc for Qt programs), some other preprocessor like GPP or m4, or your own other C++ program, etc... This kind of metaprogramming approach is routinely used with C++ and C since the previous century (look into Qt moc, bison, SWIG, ... for examples).

See also this answer to a related question (with C).

You would generate some header containing stuff like

#define a1  123
#define a2  6543
#define a3  12

(you don't want = in such #define-s)

or you might generate some enum like

enum number_en {
 a1= 123,
 a2= 6543,
 a3= 12,
};

Notice that generating C++ code could be a more efficient (since build-time) solution at runtime than other (e.g. std::map based) approaches.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
1

Macro are parsed by the preprocessor and are unknown to the processor (you can't use them in your code).

If you want to associate a name to a value, you can use a const map and constexpr expression :

constexpr std::string a1="a1";
constexpr std::string a2="a2";
constexpr std::string a3="a3";

const std::map<std::string, int> = {
    {a1, 123},
    {a2, 6543},
    {a3, 12} 
}

You need C++11 (or above) in order for this to work.

Clonk
  • 2,025
  • 1
  • 11
  • 26
1

Prefer const variable over MACRO

constexpr auto a1 = 123;
constexpr auto a2 = 6543;
constexpr auto a3 = 12;

Then to iterate over them, no need of the name, just do

for (auto e : {a1, a2, a3}) {
    func(e);
}

You might want to create a variable for the list to avoid repetition if you iterate at several place.

Jarod42
  • 203,559
  • 14
  • 181
  • 302