13

Possible Duplicate:
Is it a good idea to wrap an #include in a namespace block?

I've got a project with a class log in the global namespace (::log).

So, naturally, after #include <cmath>, the compiler gives an error message each time I try to instantiate an object of my log class, because <cmath> pollutes the global namespace with lots of three-letter methods, one of them being the logarithm function log().

So there are three possible solutions, each having their unique ugly side-effects.

  • Move the log class to it's own namespace and always access it with it's fully qualified name. I really want to avoid this because the logger should be as convenient as possible to use.
  • Write a mathwrapper.cpp file which is the only file in the project that includes <cmath>, and makes all the required <cmath> functions available through wrappers in a namespace math. I don't want to use this approach because I have to write a wrapper for every single required math function, and it would add additional call penalty (cancelled out partially by the -flto compiler flag)
  • The solution I'm currently considering:

Replace

#include <cmath>

by

namespace math {
#include "math.h"
}

and then calculating the logarithm function via math::log().

I have tried it out and it does, indeed, compile, link and run as expected. It does, however, have multiple downsides:

  • It's (obviously) impossible to use <cmath>, because the <cmath> code accesses the functions by their fully qualified names, and it's deprecated to use in C++.
  • I've got a really, really bad feeling about it, like I'm gonna get attacked and eaten alive by raptors.

So my question is:

  • Is there any recommendation/convention/etc that forbid putting include directives in namespaces?
  • Could anything go wrong with

    • diferent C standard library implementations (I use glibc),
    • different compilers (I use g++ 4.7, -std=c++11),
    • linking?
  • Have you ever tried doing this?
  • Are there any alternate ways to banish the math functions from the global namespace?

I've found several similar questions on stackoverflow, but most were about including other C++ headers, which obviously is a bad idea, and those that weren't made contradictory statements about linking behaviour for C libraries. Also, would it be beneficial to additionally put the #include <math.h> inside extern "C" {}?

edit

So I decided to do what probably everyone else is doing, and put all of my code in a project namespace, and to access the logger with it's fully qualified name when including <cmath>.

Community
  • 1
  • 1
mic_e
  • 5,594
  • 4
  • 34
  • 49
  • So wrong. For so many reasons :) You *can* do it (it'll compile, and it might even look like it does what you think you're trying to do) ... but you *shouldn't*. The *correct* thing is to put your namespace declarations *inside* each header file. IMHO... – paulsm4 Sep 07 '12 at 19:49
  • @paulsm4 Why? You cite "so many reasons" but then do not supply any. As we are obviously not going to go and edit the standard headers on every system we compile on nor are we going to distribute our own it seems a very logical use of the name-spacing feature to take c functions out of the global namespace. – cptaffe Apr 24 '15 at 05:35

4 Answers4

18

No, the solution that you are considering is not allowed. In practice what it means is that you are changing the meaning of the header file. You are changing all of its declarations to declare differently named functions.

These altered declarations won't match the actual names of the standard library functions so, at link time, none of the standard library functions will resolve calls to the functions declared by the altered declarations unless they happen to have been declared extern "C" which is allowed - but not recommended - for names which come from the C standard library.

ISO/IEC 14882:2011 17.6.2.2/3 [using.headers] applies to the C standard library headers as they are part of the C++ standard library:

A translation unit shall include a header only outside of any external declaration or definition[*], and shall include the header lexically before the first reference in that translation unit to any of the entities declared in that header.

[*] which would include a namespace definition.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • Accepted for ISO/IEC standard quote. Still deciding if I'll go with solution #0 or solution #1 then. – mic_e Sep 07 '12 at 20:11
  • "at link time, none of the standard library functions will resolve calls to the functions declared by the altered declarations." So why don't I get any linker errors? – mic_e Sep 07 '12 at 20:16
  • @mic_e: Which functions do you actually use from ``? Some may be provided as macros. – CB Bailey Sep 07 '12 at 20:20
  • I'm using expf, and as you can see in this paste: http://pastebin.com/0vnSud4L the method is linked, and the linking succeeds. – mic_e Sep 07 '12 at 20:48
  • 1
    @mic_e: It's likely that the function that you use is declared `extern "C"` in `math.h`. Although probably _very_ common in combined C and C++ implementations, technically you can't rely on this. From 17.2.6.3: "Whether a name from the C standard library declared with external linkage has `extern "C"` or `extern "C++"` linkage is implementation-defined. It is recommended that an implementation use `extern "C++"` linkage for this purpose". – CB Bailey Sep 07 '12 at 21:28
6

Why not putting a log class in it's own namespace and using typedef namespace::log logger; to avoid name clashes in a more convenient way?

parallelgeek
  • 438
  • 2
  • 11
  • I could also just `using namespace namespace`, and call the math function via `::log()`, while calling my class via `log()` or `namespace::log()` – mic_e Sep 07 '12 at 20:57
2

Change your class's name. Not that big of a deal. ;-)

Seriously though, it's not a great idea to put names in the global namespace that collide with names from any standard header. C++03 didn't explicitly permit <cmath> to define ::log. But implementations were chronically non-conforming about that due to the practicalities of defining <cmath> on top of an existing <math.h> (and perhaps also an existing static-link library for some headers, including math). So C++11 ratifies existing practice, and allows <cmath> to dump everything into the global namespace. C++11 also reserves all those names for use with extern "C" linkage, and all function signatures for use with C++ linkage, even if you don't include the header. But more on that later.

Because in C++ any standard header is allowed to define the names from any other standard header (i.e, they're allowed to include each other), this means that any standard header at all can define ::log. So don't use it.

The answer to your question about different implementations is that even if your scheme works to begin with (which isn't guaranteed), in some other implementation there might be a header that you use (or want to use in future in the same TU as your log class), that includes <cmath>, and that you didn't give the namespace math treatment to. Off the top of my head, <random> seems to me a candidate. It provides a whole bunch of continuous random number distributions that plausibly could be implemented inline with math functions.

I suggest Log, but then I like capitalized class names. Partly because they're always distinct from standard types and functions.

Another possibility is to define your class as before and use struct log in place of log. This doesn't clash with the function, for reasons that only become clear if you spend way too much time with the C and C++ standards (you only use log as a class name, not as a function and not as a name with "C" linkage, so you don't infringe on the reserved name. Despite all appearances to the contrary, class names in C++ still inhabit a parallel universe from other names, rather like struct tags do in C).

Unfortunately struct log isn't a simple-type-identifier, so for example you can't create a temporary with struct log(VERY_VERBOSE, TO_FILE). To define a simple-type-identifier:

typedef struct log Log;
Log(VERY_VERBOSE, TO_FILE); // unused temporary object

An example of what I say in a comment below, based on a stated example usage. I think this is valid, but I'm not certain:

#include <iostream>
#include <cmath>
using std::log; // to enforce roughly what the compiler does anyway

enum Foo {
    foo, bar
};

std::ostream &log(Foo f) { return std::cout; }

int main() {
    log(foo) << log(10) << "\n";
}
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Well, unfortunately, I like my class names in lower case, _because_ that's how the standard library names are written :P. The usage of my class would actually look like this: `log(lvl::ERROR) << "the logarithm of 10 is " << log(10);` – mic_e Sep 07 '12 at 22:31
  • @mic_e: well unfortunately, *I* like writing code with defined behavior ;-p. If not `Log`, how about `logg`? In your example usage though, you don't actually need `log` to be a class, it could be a C++-linkage function whose parameter type is some enum of which `lvl::ERROR` is a value. You can't overload functions in namespace `std`, but AFAIK you can overload functions in the global namespace that come from namespace `std`. I still don't advise it, but I think it flies. Probably. – Steve Jessop Sep 07 '12 at 22:41
  • Oh, and returns either an already-existing stream by reference, or an instance of the renamed log class by value. Which would need to have `operator<<` as either a member function or a non-member function taking an rvalue reference. – Steve Jessop Sep 07 '12 at 22:48
  • If you're interrested in what my class actually does, take a look at this simplified version: http://pastebin.com/Ndg20FJ8 Unfortunately I have already tried to do this with a function, but to no avail, since the destructor call plays a central role in the functionality, and I must hence avoid extra return value destructor calls. Also, 'defined behaviour' sounds really great :) – mic_e Sep 08 '12 at 00:42
  • @mic_e: there's really not a huge difference between a constructor call, and a function call that returns an object by value. They both create a temporary that's destroyed at the end of the full-expression, the difference is that the constructor can't use the name `log` (because the function is chosen in preference), whereas the function call can. You might get different behavior according to whether the object in the return statement is copy-elided or not, but I'd have thought you can fix that if you're willing to special-case a log that's had nothing written to it, to do nothing. – Steve Jessop Sep 08 '12 at 01:51
  • risking to take this discussion even more off-topic than it already is, there unfortunately _is_ a difference. usually the behaviour is the same, but only due to optional optimizing behaviour of common compilers: http://pastebin.com/HvJzVFaM note the additional destructor call when compiling with -fno-elide-constructors – mic_e Sep 08 '12 at 02:30
  • @mic_e: hence "I'd have thought you can avoid that if...". The question is, are you committed to the idea that `log();` should print the time in brackets, rather than printing nothing? If so, fair enough, you'll have to change the name of `log`. If not then you can keep it (but as a function), and write `log() << "";` to get the time in brackets and no message. – Steve Jessop Sep 08 '12 at 02:36
2

It is ugly hack too, but I believe will not cause any linker problems. Just redefine log name from <math.h>

 #define log math_log
 #include <math.h>
 #undef log

It could cause problems with inline functions from math using this log, but maybe you'd be lucky...

Math log() is still accessible but it's not easy. Within functions where you want to use it, just repeat its real declaration:

    int somefunc() {
        double log(double); // not sure if correct
        return log(1.1);
    }
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • unfortunately, this causes a linker error: http://pastebin.com/2HMcma7w since it tries to link against the unknown method math_log. – mic_e Sep 08 '12 at 00:21
  • I believed you don't need to use this log() function from math. At least from the files where you want to use your log – PiotrNycz Sep 08 '12 at 00:47
  • you are right. this is, indeed, an ugly hack since it makes the math log() completely inaccessible, and, in some horribly unrealistic scenario, expf() may rely on log() internally, but it is a fourth possible approach to solve my problem, and I explicitly asked for other approaches, so +1 – mic_e Sep 08 '12 at 02:37