forward declarations and namespaces?

S

Steven T. Hatton

Should I be able to forward declare something from a namespace different
from the current one? For example the following code compiles:

//testdriver.hpp
#ifndef TESTDRIVER_HPP
#define TESTDRIVER_HPP
#include <ostream>

namespace ns_testdriver{
using std::eek:stream;

class Testdriver{
public:
Testdriver(){}
virtual ~Testdriver(){}
virtual void runTest(ostream& out);
};
}
#endif

/*******************************/

//testdriver.cpp
#include "testdriver.hpp"

namespace ns_testdriver{

void Testdriver::runTest(ostream& out){
out << "This is ns_testdriver::Testdriver";
}
}


When I tried to forward declare std::eek:stream in testdriver.hpp I couldn't
figure out a way that would compile. Is this the wrong thing to try to do,
or am I just screwing it up?

The following code is one example of the different things I've tried. It
results in "error: `ostream' is already declared in this scope":

//testdriver.hpp
#ifndef TESTDRIVER_H
#define TESTDRIVER_H

namespace ns_testdriver{
using namespace std;
class ostream;

class Testdriver{
public:
Testdriver(){}
virtual ~Testdriver(){}
virtual void runTest(ostream& out);
};
}
#endif

/*******************************/
//testdriver.cpp
#include "testdriver.hpp"
#include <ostream>

namespace ns_testdriver{
using std::eek:stream;
void Testdriver::runTest(ostream& out){
out << "This is ns_testdriver::Testdriver";
}
}
 
A

Andre Kostur

Should I be able to forward declare something from a namespace
different from the current one? For example the following code
compiles:

//testdriver.hpp
#ifndef TESTDRIVER_HPP
#define TESTDRIVER_HPP
#include <ostream>

namespace ns_testdriver{
using std::eek:stream;

class Testdriver{
public:
Testdriver(){}
virtual ~Testdriver(){}
virtual void runTest(ostream& out);
};
}
#endif

/*******************************/

//testdriver.cpp
#include "testdriver.hpp"

namespace ns_testdriver{

void Testdriver::runTest(ostream& out){
out << "This is ns_testdriver::Testdriver";
}
}


When I tried to forward declare std::eek:stream in testdriver.hpp I
couldn't figure out a way that would compile. Is this the wrong thing
to try to do, or am I just screwing it up?

The following code is one example of the different things I've tried.
It results in "error: `ostream' is already declared in this scope":

ostream isn't a simple class... you may want to include <iosfwd>
instead...
 
S

Steven T. Hatton

Andre said:
ostream isn't a simple class... you may want to include <iosfwd>
instead...

My difficulties may be the result of a pedantic compiler. Even this example
from Stroustrup's TC++PL fails on gcc 3.40:
http://www.research.att.com/~bs/matrix.c

The compiler is correctly conforming to the Standard. See here for more
inof:
http://gcc.gnu.org/ml/gcc/2004-04/msg01269.html


<iosfwd> /Did/ work correctly for me. However, I now find the same problem
with /valarray/. The code listed below compiles and runs. Note, however,
<valarray> is #included in /forward.hpp/ rather than the implementation
file.


//forward.hpp
#ifndef FORWARD_HPP
#define FORWARD_HPP

#include <iosfwd>
#include <valarray>

using std::valarray;

struct V{
V(const size_t& size);
valarray<double>* va;
std::eek:stream& stringify(std::eek:stream& out);
};

#endif


//forward.cc
#include <iostream>
#include <valarray>
#include "forward.hpp"

V::V(const size_t& size){
va = new valarray<double>(1.1,size);
}

std::eek:stream& V::stringify(std::eek:stream& out){
for(size_t i = 0; i < va->size(); i++)
{
out << (*va) << " ";
}
return out;
}


//main.cc
#include "forward.hpp"
#include <iostream>
int main(int argc, char* argv[]) {
V v(10);
std::cout<<v.stringify(std::cout);
}
 
D

Dave Moore

Steven T. Hatton said:
My difficulties may be the result of a pedantic compiler. Even this example
from Stroustrup's TC++PL fails on gcc 3.40:
http://www.research.att.com/~bs/matrix.c

The compiler is correctly conforming to the Standard. See here for more
inof:
http://gcc.gnu.org/ml/gcc/2004-04/msg01269.html


<iosfwd> /Did/ work correctly for me. However, I now find the same problem
with /valarray/. The code listed below compiles and runs. Note, however,
<valarray> is #included in /forward.hpp/ rather than the implementation
file.

As with std::eek:stream, std::valarray is not a simple class type,
therefore, an attempt to forward declare it like:

using namespace std;
class valarray;

will fail. Unfortunately there is AFAIK no mechanism like <iosfwd>
providing forward declarations of other STL classes. OTOH, you can
probably roll your own by looking at the examples in <iosfwd> ...
without checking myself, I expect something like:

using namespace std;
template <class T> class valarray;

should remove the need to include <valarray> in your header file.
IIRC, std::valarray takes only one template argument.

HTH, Dave Moore
//forward.hpp
#ifndef FORWARD_HPP
#define FORWARD_HPP

#include <iosfwd>
#include <valarray>

using std::valarray;

struct V{
V(const size_t& size);
valarray<double>* va;
std::eek:stream& stringify(std::eek:stream& out);
};

#endif


//forward.cc
#include <iostream>
#include <valarray>
#include "forward.hpp"

V::V(const size_t& size){
va = new valarray<double>(1.1,size);
}

std::eek:stream& V::stringify(std::eek:stream& out){
for(size_t i = 0; i < va->size(); i++)
{
out << (*va) << " ";
}
return out;
}


//main.cc
#include "forward.hpp"
#include <iostream>
int main(int argc, char* argv[]) {
V v(10);
std::cout<<v.stringify(std::cout);
}
 
S

Steven T. Hatton

Dave said:
As with std::eek:stream, std::valarray is not a simple class type,
therefore, an attempt to forward declare it like:

using namespace std;
class valarray;

I didn't even want to introduce the namespace at that point. I find
the /using/ directive for namespaces to be contrary to my sense of order.
I much prefer introducing specific names as needed with /using/
declaration.
will fail. Unfortunately there is AFAIK no mechanism like <iosfwd>
providing forward declarations of other STL classes. OTOH, you can
probably roll your own by looking at the examples in <iosfwd> ...
without checking myself, I expect something like:

I used something that I'm pretty sure is either explicitly undefined, or
non-conforming by introducing my own names into std:: This is the whole
ugly solution I cam up with:

/*************
Makefile
/*************
forward : forward.hpp forward.cc main.cc
g++ -o forward forward.cc main.cc
clean:
rm *.o forward



/*************
forward.cc
/*************

#include "forward.hpp"
#include <iostream>
#include <valarray>

V::V(const size_t& size){
va = new std::valarray<double>(1.1,size);
}

std::eek:stream& V::stringify(std::eek:stream& out){
for(size_t i = 0; i < va->size(); i++)
{
out << (*va) << " ";
}
return out;
}



/*************
forward.hpp
/*************

#ifndef FORWARD_HPP
#define FORWARD_HPP

#include <iosfwd>
#include "stringable.hpp"

namespace std{
template<class> class valarray;
}

struct V: public Stringable {
V(const size_t& size);
std::valarray<double>* va;
std::eek:stream& stringify(std::eek:stream& out);
};

#endif



/*************
main.cc
/*************

#include "forward.hpp"
#include <iostream>

int main(int argc, char* argv[]) {
V v(10);
std::cout << v << "\n";
}



/*************
stringable.hpp
/*************

#ifndef STRINGABLE_H
#define STRINGABLE_H
#include <iosfwd>

using std::eek:stream;

/**
An un-universal base class
*/
class Stringable {
public:
virtual ostream& stringify(ostream& out)=0;
};

inline ostream& operator<<(ostream& out, Stringable& s){
s.stringify(out);
return out;
}

#endif



using namespace std;
template <class T> class valarray;

should remove the need to include <valarray> in your header file.

That is, if I have std:: available from some other #include. As You see
from the code above, I actually opened the std namespace up, and put my own
forwar declaration in there.

I have to wonder how many problems exist with the 'solution' I presented
above.
 
D

Dave Moore

Steven T. Hatton said:
I didn't even want to introduce the namespace at that point. I find
the /using/ directive for namespaces to be contrary to my sense of order.
I much prefer introducing specific names as needed with /using/
declaration.

Ok I understand this ... I also balk at 'using' the whole std
namespace in a header file. I used to do it, but it caused some weird
problems that I never tracked down all the way, but which went away
when I switched to only putting 'using namespace std' in .cpp files.
That was a while ago with an old version of GCC, but I stuck with the
habit. Lately I have also started introducing specific names when
writing headers containing templates.
will fail. Unfortunately there is AFAIK no mechanism like <iosfwd>
providing forward declarations of other STL classes. OTOH, you can
probably roll your own by looking at the examples in <iosfwd> ...
without checking myself, I expect something like:

I used something that I'm pretty sure is either explicitly undefined, or
non-conforming by introducing my own names into std:: This is the whole
ugly solution I cam up with:
[snip]


/*************
forward.hpp
/*************

#ifndef FORWARD_HPP
#define FORWARD_HPP

#include <iosfwd>
#include "stringable.hpp"

namespace std{
template<class> class valarray;
}

[snip]

I don't see any problem with the declaration above .. you have not
injected your own name into the std namespace, you have simply
provided a forward declaration of an existing STL container with the
proper syntax. I don't see how this breaks anything, because if you
screw up the forward declaration, you will get a bucketful of
compile-time errors when you include the proper STL header in your
..cpp file. I checked this using GCC 3.3.1 by forward declaring
std::valarray with two template parameters, or std::vector with 1
template parameter, and did indeed get the compile-time errors. Now,
redefining std::valarray would definitely be a no-no, but declaring it
like you did above seems fine. BTW, I would probably comment every
such forward declaration to clarify your intent.
I have to wonder how many problems exist with the 'solution' I presented
above.

None AFAICS, at least not with the forward declaration of
std::valarray.

HTH, Dave Moore
 
D

Dave Moore

Steven T. Hatton said:
Dave Moore wrote:
[snip]
/*************
forward.hpp
/*************

#ifndef FORWARD_HPP
#define FORWARD_HPP

#include <iosfwd>
#include "stringable.hpp"

namespace std{
template<class> class valarray;
}

[snip]

I don't see any problem with the declaration above .. you have not
injected your own name into the std namespace, you have simply
provided a forward declaration of an existing STL container with the
proper syntax.

Ok .. I was just re-reading some of the "Guru of the Week" questions,
and it turns out that the above statement is incorrect .. (c.f. GotW
#34 http://www.gotw.ca/gotw/034.htm )
I don't see how this breaks anything, because if you
screw up the forward declaration, you will get a bucketful of
compile-time errors when you include the proper STL header in your
.cpp file. I checked this using GCC 3.3.1 by forward declaring
std::valarray with two template parameters, or std::vector with 1
template parameter, and did indeed get the compile-time errors.

So, what I have said above is technically correct, but it makes your
code completely non-portable, and also susceptible to (otherwise
transparent) changes to the STL implementation in later compiler
versions.

[snip]

Sorry for the misunderstanding earlier ...

HTH, Dave Moore
 

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,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top