export: should I want it?

  • Thread starter Steven T. Hatton
  • Start date
S

Steven T. Hatton

<quote
url="http://www.informit.com/guides/content.asp?g=cplusplus&seqNum=53&rl=1">

exported Templates

Last updated Sep 6, 2006.
exported Templates

The separate compilation model enables us to define functions, types and
objects in one translation unit and refer to them from other translation
units. After compiling all the translation units of a program, the linker
subsequently resolves all the references to extern symbols, producing a
single executable file. When dealing with ordinary functions and classes,
the separate compilation model works neatly.

Templates, however are a different beast. When you instantiate a template,
the compiler must see its definition. This is an explicit requirement in
the C++ standard, which states that dependent names (names that depend on
the type of a template parameter) shall be looked up in both the current
context (i.e., the place of instantiation) and the context of the
template's definition. The compiler cannot generate the code of a
specialization without looking at the template's definition ? even if the
definition appears in a separate translation unit. If the template is
defined in several source files, all the definitions must comply with the
One Definition Rule. As I explained elsewhere, the common approach is to
define the template in a header file and #include it in every source file
that uses that template.

If you have used templates before, you already know that. However, standard
C++ defines another compilation model for templates known as the separation
model.


export: How it All Began

Right from the start, C++ creators called for separate compilation of
templates. However, they didn't specify how this could be accomplished.
Unlike most other C++ features, which were standardized only after being
implemented by one vendor at least, exported templates were added to C++ in
1996 with no existing implementation whatsoever.

In 1996, many compilers hardly supported ordinary templates and the C++
community had gained little experience with advanced template techniques
such as meta-programming. Therefore, the implications of implementing
export weren't fully recognized. The very few people who have implemented
exported templates since agree that this is an arduous task indeed.


State of the Art

The most popular C++ compilers (Microsoft's Visual C++ .NET, Borland's C++
BuilderX, GNU's GCC and others) don't support export. Even vendors who have
expressed their commitment to "100% ISO compliance" exclude export from
their checklist. The frequently-cited reason for this is "lack of demand."
Could it be that users don't demand this features because they've never had
a chance to use it?

....
</quote>

One view of the "proper" use of "header files" is that they
represent "interfaces", whereas "source files" represent implementations.
Indeed some people suggest that we not have anything requiring allocation
in a header file; with the exception of (judiciously chosen) inline member
functions, and class template members[*]. That means, according to the
advice, MyClass::_data doesn't belong in a header.

class MyClass {
public:
std::eek:stream& write(std::eek:stream& out);
private:
std::string _data; //I don't belong here?
};

I have to agree that I gain a modest sense of accomplishment if I write a
class definition in a header file, and the only #include needed is to
include the definition of the base class. Any other types named can be
introduced using a forward declaration.

So what has this to do with `export'? Well, without it, all of a template
definition must go in a header file, or directly in the source file being
compiled. The motivations I am aware of for not wanting allocation defined
in a header are so that code compiled using the header won't need to be
recompiled if the implementation of the interface declared in the header
changes. Another reason is that it speeds up compile time. I'm not sure
any of that would be gained in the case of processing template code as a
direct result of implementing export. For example, would using precompiled
headers provide all the compile speed advantages that would be gained by
implementing export?

What would I gain if my compiler supported export?


[*]Of course we are also admonished not to "second guess" the implementors
of the (Standard) Library by forward declaring entities defined therein.
IOW I shouldn't try to forward declare std::string like this:

namespace std {
class string; //Wrong! Mere users should not mess with std::
}
 
S

Salt_Peter

Steven said:
<quote
url="http://www.informit.com/guides/content.asp?g=cplusplus&seqNum=53&rl=1">

exported Templates

Last updated Sep 6, 2006.
exported Templates

The separate compilation model enables us to define functions, types and
objects in one translation unit and refer to them from other translation
units. After compiling all the translation units of a program, the linker
subsequently resolves all the references to extern symbols, producing a
single executable file. When dealing with ordinary functions and classes,
the separate compilation model works neatly.

Templates, however are a different beast. When you instantiate a template,
the compiler must see its definition. This is an explicit requirement in
the C++ standard, which states that dependent names (names that depend on
the type of a template parameter) shall be looked up in both the current
context (i.e., the place of instantiation) and the context of the
template's definition. The compiler cannot generate the code of a
specialization without looking at the template's definition ? even if the
definition appears in a separate translation unit. If the template is
defined in several source files, all the definitions must comply with the
One Definition Rule. As I explained elsewhere, the common approach is to
define the template in a header file and #include it in every source file
that uses that template.

If you have used templates before, you already know that. However, standard
C++ defines another compilation model for templates known as the separation
model.


export: How it All Began

Right from the start, C++ creators called for separate compilation of
templates. However, they didn't specify how this could be accomplished.
Unlike most other C++ features, which were standardized only after being
implemented by one vendor at least, exported templates were added to C++ in
1996 with no existing implementation whatsoever.

In 1996, many compilers hardly supported ordinary templates and the C++
community had gained little experience with advanced template techniques
such as meta-programming. Therefore, the implications of implementing
export weren't fully recognized. The very few people who have implemented
exported templates since agree that this is an arduous task indeed.


State of the Art

The most popular C++ compilers (Microsoft's Visual C++ .NET, Borland's C++
BuilderX, GNU's GCC and others) don't support export. Even vendors who have
expressed their commitment to "100% ISO compliance" exclude export from
their checklist. The frequently-cited reason for this is "lack of demand."
Could it be that users don't demand this features because they've never had
a chance to use it?

...
</quote>

One view of the "proper" use of "header files" is that they
represent "interfaces", whereas "source files" represent implementations.
Indeed some people suggest that we not have anything requiring allocation
in a header file; with the exception of (judiciously chosen) inline member
functions, and class template members[*]. That means, according to the
advice, MyClass::_data doesn't belong in a header.

class MyClass {
public:
std::eek:stream& write(std::eek:stream& out);
private:
std::string _data; //I don't belong here?
};

What makes you think that _data is allocated here? Its not. The
compiler will generate a ctor and implement the allocation of _data
whether or not you declare/define any ctors. Also, from the compiler's
perspective, the header does not exist. The compilation unit, probably
MyClass.cpp in this case, will have the above header injected into it.

As far as your discussion of "interfaces" above, it sounds more like
you are talking about abstract classes. A corresponding example would
be:

// mybase.hpp
#include <ostream>

struct MyBase // abstract
{
virtual ~MyBase();
std::eek:stream& write(std::eek:stream&) = 0;
};

// MyBase.cpp
#include "MyBase.hpp"

MyBase::~MyBase() { }

// MyClass.hpp
#include <string>
#include "MyBase.hpp"

class MyClass : public MyBase
{
public:
MyClass();
std::eek:stream& write(std::eek:stream& out);
private:
std::string data;
};

// MyClass.cpp
#include "MyClass.hpp"
MyClass::MyClass(const std::string& r_s) : data(r_s)
{
}

std::eek:stream& MyClass::write(std::eek:stream& out)
{
...
}
I have to agree that I gain a modest sense of accomplishment if I write a
class definition in a header file, and the only #include needed is to
include the definition of the base class. Any other types named can be
introduced using a forward declaration.

So what has this to do with `export'? Well, without it, all of a template
definition must go in a header file, or directly in the source file being
compiled. The motivations I am aware of for not wanting allocation defined
in a header are so that code compiled using the header won't need to be
recompiled if the implementation of the interface declared in the header
changes. Another reason is that it speeds up compile time. I'm not sure
any of that would be gained in the case of processing template code as a
direct result of implementing export. For example, would using precompiled
headers provide all the compile speed advantages that would be gained by
implementing export?

Consider an often used template like:

// ttostring.hpp
#ifndef TTOSTRING_HPP_
#define TTOSTRING_HPP_
#include <string>
#include <sstream>

template< typename T >
std::string TtoString( const T& r_t )
{
std::eek:stringstream oss;
oss << r_t;
return oss.str()
}

#endif

A different version or version(s) of that template will be generated in
whatever compilation unit uses it.
What would I gain if my compiler supported export?


[*]Of course we are also admonished not to "second guess" the implementors
of the (Standard) Library by forward declaring entities defined therein.
IOW I shouldn't try to forward declare std::string like this:

namespace std {
class string; //Wrong! Mere users should not mess with std::
}

--
NOUN:1. Money or property bequeathed to another by will. 2. Something handed
down from an ancestor or a predecessor or from the past: a legacy of
religious freedom. ETYMOLOGY: MidE legacie, office of a deputy, from OF,
from ML legatia, from L legare, to depute, bequeath. www.bartleby.com/61/
 
S

Steven T. Hatton

Salt_Peter said:
One view of the "proper" use of "header files" is that they
represent "interfaces", whereas "source files" represent implementations.
Indeed some people suggest that we not have anything requiring allocation
in a header file; with the exception of (judiciously chosen) inline
member
functions, and class template members[*]. That means, according to the
advice, MyClass::_data doesn't belong in a header.

class MyClass {
public:
std::eek:stream& write(std::eek:stream& out);
private:
std::string _data; //I don't belong here?
};

What makes you think that _data is allocated here?

"C++ allows the programmer to expose the representation of a class as part
of the interface. This representation may be hidden (using /private/
or /protected/), but it is available to the compiler to allow alocation of
automatic vaiables, to allow inline substitution of functions, etc." -
TC++PL(SE).
Its not. The
compiler will generate a ctor and implement the allocation of _data
whether or not you declare/define any ctors.

The term "allocation" is used to describe the operation performed by the
compiler which determines the beginning and ending relative offsets of a
block of storage reserved to store an instance of a variable.
Also, from the compiler's
perspective, the header does not exist. The compilation unit, probably
MyClass.cpp in this case, will have the above header injected into it.

What about

//YourClass.cpp
#include <sth/MyClass>

?
As far as your discussion of "interfaces" above, it sounds more like
you are talking about abstract classes. A corresponding example would
be:

// mybase.hpp
#include <ostream>


Consider an often used template like:

// ttostring.hpp
#ifndef TTOSTRING_HPP_
#define TTOSTRING_HPP_
#include <string>
#include <sstream>

template< typename T >
std::string TtoString( const T& r_t )
{
std::eek:stringstream oss;
oss << r_t;
return oss.str()
}

#endif

A different version or version(s) of that template will be generated in
whatever compilation unit uses it.

I believe you mean to say that the template will be instantiated differently
in different compilation units depending upon the actual template
parameters used to instantiate it. How is that impacted by `export'?
 
E

eriwik

I believe you mean to say that the template will be instantiated differently
in different compilation units depending upon the actual template
parameters used to instantiate it. How is that impacted by `export'?

With export the template will only be compiled/instanciated once, and
that is in the compilation-unit of the template implementation. This
single implementation is then linked in and used wherever the template
is instanciated. This should give smaler executables, if nothing else,
since the same code is not duplicated. In template-heave programs this
can give significant space-savings, and might lead to faster
execution-times too, by better usage of the cache.

Well, at least that's how I've understand things.
 
M

Michiel.Salters

(e-mail address removed) schreef:
With export the template will only be compiled/instanciated once, and
that is in the compilation-unit of the template implementation. This
single implementation is then linked in and used wherever the template
is instanciated. This should give smaler executables, if nothing else,
since the same code is not duplicated. In template-heave programs this
can give significant space-savings, and might lead to faster
execution-times too, by better usage of the cache.

Actually, a modern compiler will remove duplicate functions at link
time, even if the
types originally were different. Enough compilers have
sizeof(int)==sizeof(long);
instantiating function templates twice when they're overloaded on both
may be
required but the result need not be linked in twice. Export is not
needed for that.

HTH,
Michiel Salters
 

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

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,783
Latest member
RickeyDort

Latest Threads

Top