GreycLab / CImg

The CImg Library is a small and open-source C++ toolkit for image processing
http://cimg.eu
Other
1.49k stars 285 forks source link

log contaminates global namespace #389

Closed 1Hyena closed 1 year ago

1Hyena commented 1 year ago

My project has a function named log in its global namespace.

Because of that, I am unable to use CImg, as CImg seems to have its own use for log.

Screenshot from 2023-07-10 10-34-26

dtschump commented 1 year ago

CImg only defines a method CImg<T>::log(). It should not induce any conflict with other log() functions. A conflict may occur if you have conflicting macros defined in your global namespace, but not functions.

1Hyena commented 1 year ago

You're right. It wasn't that, it's something else though. Here's the minimal version of my code that causes this error.

#include <iostream>
#include <functional>
#include <CImg.h>

void log(const char *text) {
    std::cerr << text << "\n";
}

bool deserialize(
    int argc, char **argv,
    const std::function<void(const char *text)>& log_callback
) {
    return true;
}

int main(int argc, char **argv) {
    if (!deserialize(argc, argv, log)) {
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

And here's the compiler error:

main.cpp: In function ‘int main(int, char**)’:
main.cpp:17:21: error: invalid initialization of reference of type ‘const std::function<void(const char*)>&’ from expression of type ‘<unresolved overloaded function type>’
   17 |     if (!deserialize(argc, argv, log)) {
      |          ~~~~~~~~~~~^~~~~~~~~~~~~~~~~
main.cpp:11:50: note: in passing argument 3 of ‘bool deserialize(int, char**, const std::function<void(const char*)>&)’
   11 |     const std::function<void(const char *text)>& log_callback
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
make[1]: *** [Makefile:41: obj/main.o] Error 1
make: *** [Makefile:18: all] Error 2

Any ideas what could be going wrong?

dtschump commented 1 year ago

No idea, sorry. Does this compiles when CImg.h is not included ?

1Hyena commented 1 year ago

Yes, then it compiles without any problems.

dtschump commented 1 year ago

It has to be something to do with log() indeed. If I rename log() to log3() in your example, it compiles.

dtschump commented 1 year ago

I have a work-around:


namespace my_log {
  void log(const char *text) {
    std::cerr << text << "\n";
  }
}

bool deserialize(
    int argc, char **argv,
    const std::function<void(const char *text)>& log_callback
) {
    return true;
}

int main(int argc, char **argv) {
  if (!deserialize(argc, argv, my_log::log)) {
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

but I still don't get why using just log doesn't work.

1Hyena commented 1 year ago

Thanks, I changed the name of the function, for now, but in the long term this should be fixed on the library side. Unfortunately I also don't have a clue why this happens. The following lines from CImg.h are suspicious though:


#define _cimg_create_pointwise_function(name) \
  template<typename T> \
  inline CImg<_cimg_Tfloat> name(const CImg<T>& instance) { \
    return instance.get_##name(); \
  }

  _cimg_create_pointwise_function(sqr)
  _cimg_create_pointwise_function(sqrt)
  _cimg_create_pointwise_function(erf)
  _cimg_create_pointwise_function(exp)
  _cimg_create_pointwise_function(log)
dtschump commented 1 year ago

Yes, these line actually creates a log() function for CImg<T> objects, so that a user can do this:

CImg<> img("filename.bmp");
CImg<> img_log = log(img);

But removing the line _cimg_create_pointwise_function(log) does not solve the compilation issue.

dtschump commented 1 year ago

OK, so I've found the problem. It doesn't come from CImg, but from cmath. Replacing #include "CImg.h" by #include <cmath> raises exactly the same problem. (and of course, CImg actually includes cmath by default).

1Hyena commented 1 year ago

Interesting. I thought the c prefix headers such as cmath enabled their respective functions via the std namespace. So one would call std::log instead of just log.

dtschump commented 1 year ago

In theory yes, but I think g++ is a bit sloppy about that rule by default, so using log() rather than std::log() still works.

1Hyena commented 1 year ago

It's unfortunate that such things happen as they are tedious to track down but there's not much we can do about it. It is what it is. I recall having similar issues elsewhere too. Would be great if CImg didn't even include such toxic headers but sometimes it's practically impossible.