4

I'm trying to figure where in a bunch of code I'm missing a closing-brace for a namespace (i.e. getting a:

At end of source: error: expected a "}"

error). Maybe there are smarter ways to go about it, but - I want to try and check, at different points in my code, what the current namespace is. I don't need a macro; and I don't need a string; and I don't need something that will exist at run-time. I just want to get the compiler to somehow print the namespace. It's fine by me if it's in an #error, a #warning, or some construct which, failing to compile, produces an error with the namespace in it.

Example I want to generalize:

namespace ns1 { }
namespace ns2 {
namespace ns3 {

// MAGIC GOES HERE

}

this source file is missing a } somewhere. But - did I forget to close ns1? Or maybe ns2? I don't remember - they are far away from the line I'm interested in. So I want to insert something magical, that will tell me what namespace I'm in right now. Well, for this example, I can write:

namespace ns1 { }
namespace ns2 {
namespace ns3 {

void ns_detector() { return 0; }

}

and with GCC 6.3, get the error:

b.cpp: In function ‘void ns2::ns3::ns_detector()’:
b.cpp:5:29: error: return-statement with a value, in function returning 'void' [-fpermissive]
 void ns_detector() { return 0; }
                             ^
b.cpp: At global scope:
b.cpp:7:1: error: expected ‘}’ at end of input
 }
 ^

The first line of that error tells me what I need to know: It's ns2 that hasn't been closed. But - this is not so reliable. For longer, more complex pieces of code, that I compile with nvcc, using the "ns detector" function only gives me something like:

/path/to/file.cu(135): error: return value type does not match the function type

At end of source: error: expected a "}"

So, I need something more robust.

Notes:

  • The solution needs to be a fixed piece of code I paste into my own code where I want to check what the namespace is, and possibly additional compiler flags to use.
  • The solution should work for as many of: gcc, clang, nvcc, msvc, icc as possible. I particularly care about nvcc (the CUDA compiler) and gcc.
  • I'd prefer the least amount of "noise" possible other than the namespace and some "marker text" I'll look for in the output.
  • Somewhat related question: How to get string for current namespace in Macro
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 1
    If the code can't compile, how can you know hat compile-time entities such as namespaces are valid or invalid? –  May 05 '19 at 22:24
  • @NeilButterworth: I don't care whether they're invalid, I just want to know, for a given line, what namespace the compiler would put a definition on this line in. – einpoklum May 05 '19 at 22:28
  • If you don't know whether or not they are invalid, how can you know if things can be placed in them? –  May 05 '19 at 22:30
  • @NeilButterworth: See example; although it's not quite an MCVE. – einpoklum May 05 '19 at 22:41
  • A text editor with good auto-indentation helps for these kinds of errors. And only writing small pieces of code before attempting to build, as it narrows down the possible places where such errors could happen. And source-code control like Git or Subversion, as then it's easy to go back in code change history until the error disappears and then do a diff forward to see the changes. – Some programmer dude May 05 '19 at 22:42
  • @Someprogrammerdude: 1. Once you're working on more than one file, and have curly braces within #ifdef's, a text editor (probably) won't do the trick. 2. About version-control - already doing that. The problem is that some of my code is being pushed into a namespace, but some isn't, and there are complex dependencies for namespace resolution, with some sub-namespaces being shared, etc. etc. – einpoklum May 05 '19 at 22:44

2 Answers2

2

This will depend on compilers and their versions. One that currently works for all gcc, clang, icc and msvc (let us know if it works for nvcc!) is accessing a missing data member:

struct A { A() { this->a; } };

All compilers will report an error with the qualified name, e.g.:

error: no member named 'a' in 'foo::bar::A'

So you can easily grep for ::A' or ::A" and extract it (all those 4 major compilers put the name between quotes; otherwise, simply use a unique identifier that you won't find anywhere else etc.).

Acorn
  • 24,970
  • 5
  • 40
  • 69
  • See my attempt to generalize your solution a bit. And - +1. – einpoklum May 05 '19 at 22:57
  • @einpoklum Thanks -- indeed, my answer was intended to solve the core question, i.e. to provide a primitive, not to provide a full fledged library-like approach :) Maybe you can publish that into a quick header library in GitHub (which probably will require anyway updates as compilers evolve...), although I hope people don't need this too often! – Acorn May 05 '19 at 23:02
2

A (possible) improvement on @Acorn's good and simple solution, which can be used many times, for detection in multiple places in the same file:

#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)
#define UNIQUE_IDENTIFIER(prefix) EXPAND_THEN_CONCATENATE(prefix, __LINE__)

#define DETECT_NAMESPACE \
struct UNIQUE_IDENTIFIER(namespace_detector_on_line_) { \
    void f() { look_at_the_class_name_above = 0; } \
};

Now you should be able to write DETECT_NAMESPACE on many different lines. Also, the macro definitions can be placed in a separate file.

For the code in the example, this will give:

b.cpp: In member function ‘void ns2::ns3::namespace_detector_on_line_13::f()’:
b.cpp:5:93: error: ‘look_at_the_class_name_above’ was not declared in this scope
 #define DETECT_NAMESPACE struct UNIQUE_IDENTIFIER(namespace_detector_on_line_) { void f() { look_at_the_class_name_above! = 0; } };
                                                                                             ^
b.cpp:13:1: note: in expansion of macro ‘DETECT_NAMESPACE’
 DETECT_NAMESPACE
 ^~~~~~~~~~~~~~~~
b.cpp: At global scope:
b.cpp:15:1: error: expected ‘}’ at end of input
 }
einpoklum
  • 118,144
  • 57
  • 340
  • 684