An issue of visibility and "identity" of classes

U

user

Hi.
Sorry for the probable dumbness of my question, but the behaviour I'm
observing seems a blatant defect in the C++ Standard or in the
implementation I'm using (several different versions of GCC, all
behaving exactly the same, GNU binutils, GNU/Linux on PowerPC).

Let's say I have two compilation units, a.cc and b.cc; each of them
need to perform some initialization (or finalization, for what it
matters) which I want to be transparent, i.e. not visible outside the
module. A solution is defining in each of a.cc and b.cc an Initializer
class with a constructor (or destructor in the case of finalization);
for each initializer class there will be one and only one static
instance, in the respective compilation unit.
Note that each instance of the initializer classes cannot see the
other instance, nor the other class. Let's say I don't care about the
order in which the two initializations are performed.

The issue is that if I happen to give the same name to both
'initializer' classes, I see that the constructor of *one* class is
executed twice. Using different names or different namespaces for
initializer classes solves the probem, but I feel the situation is
however ugly, because a user unaware of the initializer class in some
compilation unit can make a mess. The implementation 'escapes' the
interface border, which is bad.

I don't know what the Standard has to say about this matter; however
even if the Standard dictated the semantics that we would expect I doubt
that the problem could be solved, even at the compiler level with a
different mangling which included the compilation unit name: for example
there could be several files with the same name in different
directories; and a single directory can have multiple name in any
filesystem supporting symlinks.

The linker calls the constructor defined in the *first* compilation
unit found in the command line:

[luca@ibook /tmp]$ g++ -Wall a.cc b.cc && ./a.out
Initialization performed (1)
Initialization performed (1)
Executed code from b.cc

[luca@ibook /tmp]$ g++ -Wall b.cc a.cc && ./a.out
Initialization performed (2)
Initialization performed (2)
Executed code from b.cc



Here is the distilled code:

/* ------------------------------- a.cc --------------------------- */
#include <iostream>

class Initializer{
public:
Initializer(){
std::cerr << "Initialization performed (1)\n";
}
}; // class
static Initializer theInitializer1;

void fromB(); // declaration of something in b.cc

int main(){
fromB();
return 0;
}
/* ------------------------------- a.cc (end)---------------------- */


/* ------------------------------- b.cc --------------------------- */
#include <iostream>

void fromB(){
std::cerr << "Executed code from b.cc\n";
}

class Initializer{
public:
Initializer(){
std::cerr << "Initialization performed (2)\n";
} // class
};
/* The constructor in a.cc is called!!! */
static Initializer theInitializer2;
/* ------------------------------- b.cc (end) --------------------- */

Does a reasonable solution exist in C++?
From an 'academic' point of view, how could it be implemented, even
if not allowed by the C++ Standard?

Thanks in advance,
 
D

David Hilsee

Hi.
Sorry for the probable dumbness of my question, but the behaviour I'm
observing seems a blatant defect in the C++ Standard or in the
implementation I'm using (several different versions of GCC, all
behaving exactly the same, GNU binutils, GNU/Linux on PowerPC).

Let's say I have two compilation units, a.cc and b.cc; each of them
need to perform some initialization (or finalization, for what it
matters) which I want to be transparent, i.e. not visible outside the
module. A solution is defining in each of a.cc and b.cc an Initializer
class with a constructor (or destructor in the case of finalization);
for each initializer class there will be one and only one static
instance, in the respective compilation unit.
Note that each instance of the initializer classes cannot see the
other instance, nor the other class. Let's say I don't care about the
order in which the two initializations are performed.

The issue is that if I happen to give the same name to both
'initializer' classes, I see that the constructor of *one* class is
executed twice. Using different names or different namespaces for
initializer classes solves the probem, but I feel the situation is
however ugly, because a user unaware of the initializer class in some
compilation unit can make a mess. The implementation 'escapes' the
interface border, which is bad.

I don't know what the Standard has to say about this matter; however
even if the Standard dictated the semantics that we would expect I doubt
that the problem could be solved, even at the compiler level with a
different mangling which included the compilation unit name: for example
there could be several files with the same name in different
directories; and a single directory can have multiple name in any
filesystem supporting symlinks.

The linker calls the constructor defined in the *first* compilation
unit found in the command line:

[luca@ibook /tmp]$ g++ -Wall a.cc b.cc && ./a.out
Initialization performed (1)
Initialization performed (1)
Executed code from b.cc

[luca@ibook /tmp]$ g++ -Wall b.cc a.cc && ./a.out
Initialization performed (2)
Initialization performed (2)
Executed code from b.cc



Here is the distilled code:

/* ------------------------------- a.cc --------------------------- */
#include <iostream>

class Initializer{
public:
Initializer(){
std::cerr << "Initialization performed (1)\n";
}
}; // class
static Initializer theInitializer1;

void fromB(); // declaration of something in b.cc

int main(){
fromB();
return 0;
}
/* ------------------------------- a.cc (end)---------------------- */


/* ------------------------------- b.cc --------------------------- */
#include <iostream>

void fromB(){
std::cerr << "Executed code from b.cc\n";
}

class Initializer{
public:
Initializer(){
std::cerr << "Initialization performed (2)\n";
} // class
};
/* The constructor in a.cc is called!!! */
static Initializer theInitializer2;
/* ------------------------------- b.cc (end) --------------------- */

Does a reasonable solution exist in C++?
From an 'academic' point of view, how could it be implemented, even
if not allowed by the C++ Standard?

Your code violtes the One Definition Rule, corvered in section 3.2 of the
standard. In a nutshell, you can't have the same variable, function, class
etc defined in separate translation units with different definitions.

The solution is simple: wrap stuff that shouldn't be visible outside the
translation unit inside an anonymous namespace.

namespace {
class Initializer {
// ...
};

Initializer theInitializer;
}

Depending on what you're doing, you could also use the static keyword, as
you have already shown in your example code.
 
U

user

David said:
Your code violtes the One Definition Rule, corvered in section 3.2 of the
standard.
Took note of that, thanks. I strongly dislike the "no diagnostic
required" part :), even if it surely simplifies compiler implementations.
The solution is simple: wrap stuff that shouldn't be visible outside the
translation unit inside an anonymous namespace
Thank you very much, this is exactly the clean solution I was looking
for; the anonymous namespace behaves differently than other namespaces.
I'm still curious about how this is implemented, but I'm going to look
at the generated assembly files. I'm going to come back if I don't
understand from that.
Depending on what you're doing, you could also use the static keyword, as
you have already shown in your example code.
It would be nice to have a "static" modifier for *classes* (to mean
the same we get in this case with the anonymous namespace, but with less
visual clutter), however there isn't any. The static modifier for
*instances* is correct but doesn't really solve my issue. Moreover a
conflict of instances would be detected at link time.

Thanks again,
 
D

David Hilsee

Thank you very much, this is exactly the clean solution I was looking
for; the anonymous namespace behaves differently than other namespaces.
I'm still curious about how this is implemented, but I'm going to look
at the generated assembly files. I'm going to come back if I don't
understand from that.

In TC++PL (special edition), section 8.2.5.1 ("Unnamed Namespaces"),
Stroustrup offers a good explanation. He basically says that it's
equivalent to a compiler-generated namespace that is unique to the
translation unit, making it so there is no way a member of an unnamed
namespace can be named from another translation unit. <plug> It's an
excellent book that should be considered required reading. </plug>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top