-1

Compiling a C++ based project that is utilizing an f2c.h header file, within the header file is a macro definition for min and max as follows...

#ifndef min
#define min(a,b) ((a) <= (b) ? (a) : (b))
#endif
#ifndef max
#define max(a,b) ((a) >= (b) ? (a) : (b))
#endif

This code had no issues when I was compiling and running on windows with Microsoft visual studios. Now I am trying to compile and run on LINUX with the g++ compiler, and I am getting the following error messages...

In file included from tsdriver.cpp:68:0:
/usr/include/c++/6/bits/fstream.tcc: In member function ‘virtual 
std::basic_filebuf<_CharT, _Traits>::int_type std::basic_filebuf<_CharT, 
_Traits>::underflow()’:
f2c.h:155:18: error: expected unqualified-id before ‘(’ token
#define min(a,b) ((a) <= (b) ? (a) : (b))

Now, it's only giving me the error messages for the min macro, but I believe that this is due to the compiler error being such that compilation must then be terminated.

I have done some testing and found that min/max can be called from a small test.cpp program without any extra includes, so initially thinking the error was coming from a redefinition, but that shouldn't matter since #ifndef would prevent this?

I'm not sure how to remedy this, and I'm still adjusting to a LINUX development environment. Any help is appreciated.

Thanks.

Jeremy S.
  • 2,841
  • 4
  • 10
  • 20
  • Remove `using namespace std;` wherever you applied it with your code. – πάντα ῥεῖ Aug 01 '18 at 20:47
  • Also make sure `#include "f2c.h"` is done after all of the standard headers. – NathanOliver Aug 01 '18 at 20:48
  • πάντα ῥεῖ, are you referring to removing namespace std from the f2c.h header file? – Jeremy S. Aug 01 '18 at 20:50
  • NathanOliver that was it, interesting that this isn't an issue with VS. – Jeremy S. Aug 01 '18 at 20:57
  • @JeremyStalmer Others alluded to it, but there is already a [std::min](https://en.cppreference.com/w/cpp/algorithm/min) and `std::max` function in C++ in the `` header. Your macro versions are interfering with those declarations. Best bet is to remove the C-macros and just use `std::min` and `std::max`. Also, this is the case for why `using namespace std;` is dangerous -- usage of the ubiquitous C-macros `min` and `max`, and the `std::min` and `std::max` functions. – PaulMcKenzie Aug 01 '18 at 21:23
  • See also https://stackoverflow.com/a/51165683/2785528 – 2785528 Aug 01 '18 at 21:24

1 Answers1

2

Congratulations, you just learned the hard way why macros are evil ;) The problem with macros is that they are really not much more than an automated text replacement mechanism that alters the source code before the "actual" compiler gets to see it. They do not respect things like namespaces or scope in general, because they operate at a level where these concepts don't even exist yet. C++ inherited macros from C. However, with constexpr, inline, and templates, C++ has much better ways of solving most problems for which you would typically use macros in C. While there may arguably be a few legitimate use cases for macros in C++, macros are generally best avoided if at all possible.

So what happened up there when you compiled your code with g++? Well, line 396 of fstream.tcc, which is part of the standard library implementation you're using, looks like this:

__ilen = std::min(__avail, __buflen);

Most likely, you included some part of the standard library that indirectly includes fstream.tcc. Most likely, you included your header with the macro definition

#define max(a,b) ((a) >= (b) ? (a) : (b))

before including that standard header. That means by the time the compiler makes its way through fstream.tcc, you have max defined as a function-like macro. The compiler will, thus, replace all occurrences of a token sequence that matches max (,) accordingly. That means instead of

__ilen = std::min(__avail, __buflen);

your standard header suddenly reads:

__ilen = std::((__avail) >= (__buflen) ? (__avail) : (__buflen))(__avail, __buflen);
//            ^--- note this part

This also explains the weird error message:

expected unqualified-id before ‘(’ token

std::( is a syntax error. The compiler expected another identifier following the ::. And there originally was one. But the macro replaced that identifier with garbage. Because all macros do is replace tokens with no regard to syntactical context. And since macros just silently rewrite your code for you, you never get to see the code that the "actual" compiler sees. When something goes wrong, all you get to see is the error messages the rewritten code produced. (Unless you tell your compiler to please dump out the result of preprocessing and sift through the ocean of text you get as a result.)

The best way to resolve the issue: get rid of your macros and just use std::min() and std::max. If that is not so easily done, it should at least be possible to turn these macros into inline functions instead.

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39