How to resolve int variable before passing to C/C+

2019-03-07 04:29发布

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?

3条回答
Fickle 薄情
2楼-- · 2019-03-07 04:31

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.
查看更多
The star\"
3楼-- · 2019-03-07 04:34

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;
}
查看更多
老娘就宠你
4楼-- · 2019-03-07 04:37

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.

查看更多
登录 后发表回答