-4

I am trying to execute the following code:

#define channel1 10
#define channel(id) channel##id

int main(){
    int id = 1;
    cout << channel(id)<<"\n";

    return 0;
}

I get the following error: error: use of undeclared identifier 'channelid'

Instead, I want an output to be 10, as channel(id) should be preprocessed to channel1 and which replaces the value with 10.

Is there any way to achieve it?

14
  • 4
    The preprocessor has no notion about variable values, it merely does text replacement. Commented Sep 10, 2018 at 17:00
  • 3
    Why use a macro like #define channel1 10 when you can use a proper variable like static constexpr int channel1 = 10; or static const int channel1 = 10;? Using a macro has no up-sides but many down-sides. And just use regular functions for channel(id) (possibly template functions). Stop using macros (please) :-) Commented Sep 10, 2018 at 17:03
  • 1
    Preprocessed. How do you expect the preprocessor to know what the value of the runtime variable id is? Commented Sep 10, 2018 at 17:06
  • 1
    The string sum(x, y) gets translated before compiling into the string x+y. It knows about the string, not about the meaning of the string. Commented Sep 10, 2018 at 17:06
  • 2
    @ShubhamAgrawal "But when I use + operator instead ..." No, there's no variable resolved, the only thing that happens is that cout << sum(x, y)<<"\n"; is replaced with cout << x + y <<"\n"; Commented Sep 10, 2018 at 17:08

3 Answers 3

8

The problem is caused because you're trying to mix information that it is considered at different stages in the processing of the code.

Macros and all CPP (C-Pre-Processor) stuff happens (as its own name indicates) before anything else. It doesn't know anything about the variable values (at most, about #define) and most of what it does is text wrangling.

Some of the variable values might be known at compile time (see constexpr) ... but most of them will only be known at run time.

So, in summary your code fails because the preprocessor knows nothing about the id variable.

Edit: Explaining the sum example.

We have this code x.cpp

#define sum(a,b) a + b

int main(int argc, char **argv) {
 int x = 1, y = 2;
 return sum(x,y);    
}

And this code compiles and works fine.

Let's look at what happens behind the scenes.

All C/C++ source files are preprocessed. This means that they are evaluated with a different language than either C or C++: the CPP (C-preprocessor). This CPP is the one responsible of all the #... stuff (for example searching and including the header files) and as I said, has nothing to do with C or C++.

In fact, you can even run it without the compiler (try cpp x.cpp) or you can instruct the compiler to only execute this phase (g++ -o x.i -E x.cpp)

If we look into x.i:

# 1 "x.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "x.cpp"


int main(int argc, char **argv) {
 int x = 1, y = 2;
 return x + y;

}

We can observe several things:

  1. There's a number of additional '#' lines. Those are used by the compiler to keep track of where everything came from and what was the origin of the included bits, in order to be able to provide meaningful error messages.
  2. Note how our sum macro has been replaced in the code. This is done blindly and the resulting string turned out to be incorrect C/C++ syntax it will not be detected yet, but only when the actual C/C++ parsing is done.
Sign up to request clarification or add additional context in comments.

4 Comments

Just a question, then how does this programs gives expected output, #define sum(a, b) a+b int main(){ int x = 1, y = 2; cout << sum (x, y)<<endl; }
@ShubhamAgrawal It works because sum (x, y) is replaced by x+y by the preprocessor.
@ShubhamAgrawal added a more detailed explanation with your sum(a,b)example
@ShubhamAgrawal anything related to preprocessor, to # metacommands is done before program is compiled and executed. In your case you need an array of some kind, you can't solve run-time problem like that
7

The solution is, quite simply, not to do this.

A macro cannot work with values presented minutes, hours, days, years, potentially decades later. A macro is resolved before compilation, and before compilation there is no way of knowing what value id holds. During compilation sort of but even then it gets complicated unless the initialiser is trivial and the variable's value can never change.

Instead, use a function:

#include <iostream>
#include <stdexcept>

int channel(const int id)
{
   // Your own rules here; we don't know enough about them to know.
   // Perhaps instead of a switch, a map. It depends.
   switch (id)
   {
      case 1:  return 10;
      default: throw std::out_of_range("Dammit");
   }
}

int main()
{
    int id = 1;
    std::cout << channel(id) << '\n';
}

Again, you can make it work at compile-time if id is known at compile-time, and indicated as such using the keyword constexpr:

#include <iostream>
#include <stdexcept>

template <int id>
int channel()
{
   // Perhaps instead of a switch, a sequence of 'if constexpr' and
   // a static_assert(false) at the end.
   switch (id)
   {
      case 1:  return 10;
      default: throw std::out_of_range("Rats");
   }

}

int main()
{
    constexpr int id = 1;
    std::cout << channel<id>() << '\n';
}

If neither approach is acceptable, then you must rework your requirements.

But, without knowing what those requirements actually are, we cannot help you to do that.

2 Comments

How far can an approach like this go down the constexpr road?
@tadman: Very if channel can now be a function template
2

You need another layer of indirection and for id to be a macro, not a variable (it can't work with a variable -- macros work at the token level; they can't know the value of a C variable, only the value of a preprocessor macro).

The following modified code works (i.e., prints 10):

#include <iostream>
using namespace std;

#define channel1 10
#define channel_(id) channel##id
#define channel(id) channel_(id)
#define id 1
int main(){
        cout << channel(id)<<"\n"; 
        return 0;
}

6 Comments

Thank you for the solution. But, I do not want to define id as a macro. I want it to be variable.
This only works because you completely changed the input.
@ShubhamAgrawal I want pigs to fly but they won't without some pretty heavy-weight genetic modification.
@PSkocik To be fair, we don't know either way. I suppose which direction (literally) the OP takes their code depends on the requirements we don't know
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.