0
#include <iostream>
#include <vector>
#define _SET_ENV_VAL_(F,v) set_##F##Env(v)

void setAAAEnv(int val) {
    
}
void setBBBEnv(int val) {
    
}
void setCCCEnv(int val) {
    
}
int main() {
    // Write C++ code here
    std::cout << "Hello world!";
    std::vector<std::string> funcs {"AAA", "BBB", "CCC"};
    
    for(auto iter:funcs) {
        _SET_ENV_VAL_(iter, 1);
    }
    
    
    return 0;
}

I want to use the macro function "SET_ENV_VAL" to concate the function name, but I get some compile errors like below.

/tmp/gjdChSSJ2K.cpp:4:41: error: ‘set_iterEnv’ was not declared in this scope 4 | #define WX_SET_ENV_VAL(F,v) set_##F##Env(v) | ^~~~ /tmp/gjdChSSJ2K.cpp:21:9: note: in expansion of macro ‘WX_SET_ENV_VAL’ 21 | WX_SET_ENV_VAL(iter, 1); |

How to fix this error and make the macro function working as expected?

Charlie
  • 53
  • 4
  • 3
    On an unrelated issue, all symbols beginning with an underscore and followed by an upper.case letter is *reserved*. You should not define (as macros, variables, function, classes, etc.) any symbol with such a name. Please see e.g. [What are the rules about using an underscore in a C++ identifier?](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) for more information. – Some programmer dude Jun 05 '23 at 06:25
  • 3
    As for your problem, you can't create symbols from strings in C++. C++ just doesn't have the [introspection](https://en.wikipedia.org/wiki/Type_introspection) or the [reflection](https://en.wikipedia.org/wiki/Reflective_programming) capabilites for that. What is your actual assignment? What is the underlying and actual problem your program is trying to solve? If you ask about that direct, then perhaps we could help you solve that instead. Right now your question is too much of an [XY problem](https://en.wikipedia.org/wiki/XY_problem). – Some programmer dude Jun 05 '23 at 06:27
  • Don't do this. Write a function that accepts a name and a value. – n. m. could be an AI Jun 05 '23 at 06:29
  • 1
    As a possible workaround, why not a map, mapping the string to a value? – Some programmer dude Jun 05 '23 at 06:30
  • my idea is want to concate the function name, because the function name is always begin with "set" and end with "Env". – Charlie Jun 05 '23 at 06:35
  • 1
    The preprocessor is a compile-time only thing. It can't be used for anything at runtime. – Some programmer dude Jun 05 '23 at 06:47
  • 2
    101% of the times you think you need a macro, you actually dont. This is a [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Rather than asking about your solution (using a macro) you should ask about the initial problem. If you need to select the function to be called at runtime you cannot do it with a macro and if you need it at compile time you do not need a macro. – 463035818_is_not_an_ai Jun 05 '23 at 06:56
  • 1
    fwiw, your macro is just fine. Its just not possible to make it do what you want it to do though – 463035818_is_not_an_ai Jun 05 '23 at 06:57
  • I think the macro is not a good way for my request. If I dont need to use macro to do it, any other good method to do it? – Charlie Jun 05 '23 at 06:59
  • 1
    what exactly is "it" in "good method to do it" ? You can concatenate strings but out of the box you cannot use them to call the functions. At runtime the names of the functions are not useful. If you want to map strings to functions you need to do it yourself as shown in the answer below – 463035818_is_not_an_ai Jun 05 '23 at 07:15

1 Answers1

2

On a fundamental level you are mixing runtime with compile time concepts and that will not work anyway. Secondly try to avoid macros in C++ unless you have no other choice.

If all you want is to iterate over a set of known functions then your code could look like this : Or as some programmer dude mentions put the functions in a map

#include <iostream>
#include <vector>
#include <functional>
#include <map>

void setAAAEnv(int val) 
{

}

void setBBBEnv(int val) 
{

}

void setCCCEnv(int val) 
{

}

int main() 
{
    std::vector<std::function<void(int)>> functions{ setAAAEnv, setBBBEnv, setCCCEnv };

    std::map<std::string, std::function<void(int)>> mapped_functions
    {
        { "AAA", setAAAEnv },
        { "BBB", setBBBEnv },
        { "CCC", setCCCEnv }
    };

    for (const auto& iter : functions)
    {
        iter(1);
    }

    // or call BBB by string value
    mapped_functions.at("BBB")(1);

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19
  • My idea is want to concate the function name, because the function name is always begin with "set" and end with "Env". Is it possible to do it ? – Charlie Jun 05 '23 at 06:38
  • 1
    You could concatenate the strings using macros and let the macro come up with ONE COMPILE TIME function name. What you are trying to do in your loop is to create ONE OF MULTIPLE function names at RUNTIME and that second part is never going to work. You might be able to do something like this in python (which is evaluated at runtime) but C++ creates unmodifiable code at compile time. – Pepijn Kramer Jun 05 '23 at 06:43
  • @Charlie you could add macros into Pepijn's solution: https://godbolt.org/z/j6xG74fY6 but I'd argue the reduced readability isn't worth the few saved characters of typing – Alan Birtles Jun 05 '23 at 07:08
  • @AlanBirtles I agree, if possible just show as standard C++ as possible. At least that will be immediately readable by other people (without them having to lookup what the macro is doing) – Pepijn Kramer Jun 05 '23 at 07:41
  • Small edit replaced ["BBB"] with .at("BBB"). ["BBB"] might add an empty function if none was present at key "BBB" (can lead to unexpected bugs) – Pepijn Kramer Jun 05 '23 at 07:44