1

I want to use the <complex.h> library to speed up some calculations, which may include up to 20 complex elements and operation (+ - * /). However, my colleague has declared a struct:

struct complex {
    double a;
    double b;
};

Therefore the complex macro coming from the library cannot be used due to conflict with the above structure.

The thing is, I cannot change the name of the struct due to the vast impact on the current project. I tried using the _Complex macro instead, and remove the #include <complex.h> header, it worked but _Imaginary_I, _Complex_I or I does not work.

#include <stdio.h>      /* Standard Library of Input and Output */
// #include <complex.h>    /* I want to remove this */    

int main() {
    struct complex
    {
        double a;
        double b;
    };
    double _Complex z1 = 1.0;// + 3.0 * _Complex_I;
    double _Complex z2 = 1.0;// - 4.0 * _Complex_I;    

    // none of these work
    double _Complex z = CMPLX(0.0, -0.0);
    double _Complex z3 = 1.0 + 1.0 * _Imaginary_I;
    double _Complex z4 = 1.0 + 1.0 * _Complex_I;
    double _Complex z5 = 1.0 + 1.0 * I;    

    printf("Starting values: Z1 = %.2f + %.2fi\n", creal(z1), cimag(z1));    

}

I expected a way to distinguish between complex macros coming from the library, and the defined complex structure. Sadly I cannot change the structure, so things got unexpectedly more complicated.

My __STDC_VERSION__ is 201112L, while __VERSION__ is "4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.46.4)".

phuclv
  • 37,963
  • 15
  • 156
  • 475
thnghh
  • 81
  • 1
  • 8
  • 2
    `_Complex` is not a macro; it is a [§6.4.1 Keyword](http://port70.net/~nsz/c/c11/n1570.html#6.4.1), as is `_Imaginary`. The names suffixed with `_I` are not keywords; they are macros defined by ``. – Jonathan Leffler May 17 '19 at 16:42
  • 1
    *However, my colleague has declared a `struct complex`* Instead of jumping through hoops to dodge this basic issue, how hard would it really be to **FIX** the actual problem of misusing [a standard C identifier](https://port70.net/~nsz/c/c11/n1570.html#7.3.1p4)? Because that's the **REAL** problem, and not fixing it rewards the creation of bad code - and leaves a land mine in place to cause all kinds of problems in the future. What happens when New Coder starts on the program and doesn't know he has to `#undef complex` in order to paper over the poor decisions of the past? – Andrew Henle May 17 '19 at 16:51

3 Answers3

9

The C standard anticipated this problem and has a clause specifically for it, 7.3.1p7:

Notwithstanding the provisions of 7.1.3, a program may undefine and perhaps then redefine the macros complex, imaginary, and I.

What that means is, you can do this:

#include <stdio.h>
#include <complex.h>
#undef complex     /* after including complex.h, before declaring struct complex */

//#include <header_that_defines_struct_complex.h>
struct complex
{
    double a;
    double b;
};

And now your code can use both struct complex and double _Complex with no problems, and has access to the complex math functions declared in <complex.h>.

int main(void)
{
    // this should all be fine now
    double _Complex z = CMPLX(0.0, -0.0);
    double _Complex z1 = 1.0 + 3.0 * _Complex_I;
    double _Complex z2 = 1.0 - 4.0 * _Complex_I;    
    double _Complex z3 = 1.0 + 1.0 * _Imaginary_I;
    double _Complex z4 = 1.0 + 1.0 * _Complex_I;
    double _Complex z5 = 1.0 + 1.0 * I;    
    printf("Starting values: Z1 = %.2f + %.2fi\n", creal(z1), cimag(z1));    

    return 0;
}

Note however that there is no way to make struct complex be automatically convertible to, nor "compatible with", double _Complex, even though they probably have the same layout in memory. You will have to copy back and forth by hand.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • ... duh. Can't believe I fell on my face so close to that obvious solution. – Quentin May 17 '19 at 16:45
  • 2
    While this is currently a valid way to circumvent the definitions of the standard this is nothing that will form a solid base for future software developement. In general is not a good idea to reuse any identifier that the C standard defines for a different purpose. FYI, the C committee are currently in discussions to change these ugly keywords like `_Complex` to their "real" lowercase names. – Jens Gustedt May 17 '19 at 19:25
  • 1
    @JensGustedt Ugh, I hope they realize how bad an idea that is. Backward compatibility with ancient third party libraries that do things like this and there's nothing you can do about it is one of the only things C still has going for it. – zwol May 18 '19 at 12:41
  • Yes, I forgot that only `complex` macro is duplicated, and other components such as `creal`, `cimag` and `I` still work normally. – thnghh May 20 '19 at 14:27
2

Alternatively you can redefine the struct complex which is defined in the_other_colleagues_header

#define complex my_complex
#include <the_other_colleagues_header>
#undef complex

#include <complex.h>

Now inside your .c file you can use both complex and my_complex, and others can still use the struct complex as normal. Or write a wrapper for complex.h and call it from your app

But the better solution would be asking the other colleague to change the name. It's generally a bad idea to use standard names

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • 1
    Your solution does work, also it helps me understand another use of `undef` directive. I thought its object is only to remove `define` directive. Also, thank you for editing my question and making it better! – thnghh May 20 '19 at 14:24
2

Your actual problem is

my colleague has declared a struct:

    struct complex {
        double a;
        double b;
    };

Fix that.

All the other ways of addressing this problem create what's going to be a de facto undocumented dependency on undoing a standard C feature - the definition of the macro complex. The best your organization is going to do is probably something like "On page 4 of our coding standards [you do have coding standards?], it states that you have to #undef complex after you #include <complex.h>, then you have to #include <stomps/on/complex.h> after that". And what are the odds your organization actually does that much? And then makes sure everyone on the program knows that?

Most likely, they're not going to take the effort to do that, and they won't even come close.

You're going to wind up with a bit of undocumented tribal knowledge.

Again, does your organization even have written coding standards, and actually enforce them? If you can't asnwer yes to that, you really do need to actually fix the root cause of the problem now and not leave it buried, waiting to cause worse problems in the future.

That struct complex creates not only a dependency on undoing a standard C feature, but also an ordering dependency on #include directives and the #undef statement. There's no real way to know what could happen if those dependencies aren't strictly followed - strange results and unexplained failures years in the future can not be ruled out.

Yes, it's legal and not that hard to undefine the macro with #undef complex. But what happens when someone doesn't do that, or does that in the wrong order?

Writing and maintaining software is hard enough when you do it right. Don't make it even harder for the lifetime of the program by deliberately doing something questionable at best.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56