Preventing multiple instantiations of a templated function

D

Dave Rahardja

Hi all,

Is it possible to prevent _at compile or link time_ the mulitple instantiation
of a templated function? In other words, if there exists a function

template <typename T> void fn();

I want to prevent the user from doing this:

int main()
{
fn<int>();
fn<double>(); // <-- should cause compile time or link time error
}

It matters that there is only one instance of the function in the entire
program. It doesn't matter which type becomes instantiated, but there cannot
be two references to the function with two different type parameters.

If this is possible, then is there a way to do this for _any_ templated
entity?

-dr
 
I

Ian Collins

Dave said:
Hi all,

Is it possible to prevent _at compile or link time_ the mulitple instantiation
of a templated function? In other words, if there exists a function

template <typename T> void fn();

I want to prevent the user from doing this:

int main()
{
fn<int>();
fn<double>(); // <-- should cause compile time or link time error
}

It matters that there is only one instance of the function in the entire
program. It doesn't matter which type becomes instantiated, but there cannot
be two references to the function with two different type parameters.
Each instance of the function template is a unique function, so the
answer is probably no.

Why do you want to do this?
 
I

IR

Ian said:
Each instance of the function template is a unique function, so
the answer is probably no.

I'd add that even if you could enforce a single *specialization* of
the template in your whole program, there are chances (depending on
the quality of your compiler) that multiples *instances* of the
specialization could be generated.


Cheers,
 
D

Dave Rahardja

Each instance of the function template is a unique function, so the
answer is probably no.

Why do you want to do this?

I have a set of accessors in a library of this form:


template <class ReservationPolicy>
class Resource: private ReservationPolicy
{ // ...
};

template <class ReservationPolicy>
Resource& getResource()
{
static Resource<ReservationPolicy> resource;
return resource;
}


It's a simplistic Singleton accessor for a Resource that is implemented in
terms of some ReservationPolicy.

Problem is, it's an error to have the same resource accessed via a multitude
of ReservationPolicy's, because the accessor provides access to a single
underlying resource. A decision must be made at program design time as to what
ReservationPolicy that one resource is supposed to use. Once that decision is
made, it is an error to call a different instance of getResource().

I can prevent the problem at _runtime_ by:

#include <cassert>

// The resource.
template <class ReservationPolicy>
class Resource: private ReservationPolicy
{ /* ... */ };

struct ResourceTag {};

// Helper class that helps enforce a one-instantiation rule.
template <typename T>
class OneInstance
{
public:
OneInstance()
{ assert(++instanceCount == 1); }

private:
static int instanceCount;
};

template <typename T> int OneInstance<T>::instanceCount = 0;

// Accessor function. Only one instance of this function can be
// called without generating a runtime error.
template <ReservationPolicy>
Resource& getResource()
{
static OneInstance<ResourceTag> oneInstance;
static Resource<ReservationPolicy> resource;
return resource;
}

// Reservation policies
class Policy0 { /* ... */ };
class Policy1 { /* ... */ };

int main()
{
fn<Policy0>(); // OK
fn<Policy0>(); // OK
fn<Policy1>(); // Runtime assertion
}


But I'd like to know if I can prevent this problem at _compile or link_ time.

-dr
 
J

Jim Langston

Dave Rahardja said:
Hi all,

Is it possible to prevent _at compile or link time_ the mulitple
instantiation
of a templated function? In other words, if there exists a function

template <typename T> void fn();

I want to prevent the user from doing this:

int main()
{
fn<int>();
fn<double>(); // <-- should cause compile time or link time error
}

It matters that there is only one instance of the function in the entire
program. It doesn't matter which type becomes instantiated, but there
cannot
be two references to the function with two different type parameters.

If this is possible, then is there a way to do this for _any_ templated
entity?

-dr

I can think of a way to do it using a global and a static.

#include <iostream>
#include <string>

bool fn_created = false;

template <typename T> void fn( T Val )
{
static FirstTime = true;
if ( FirstTime )
{
if ( fn_created )
throw "Function already created using diff type!";
fn_created = true;
FirstTime = false;
}
std::cout << "Value is " << Val << "\n";

}

int main()
{
fn<int>( 10 );
fn<int>( 20 );
fn<int>( 30 );
// Uncommenting out following line will cause run time throw
// fn<double>( 40.0 );

std::string wait;
std::getline( std::cin, wait );
}
 
J

Jim Langston

Jim Langston said:
I can think of a way to do it using a global and a static.

#include <iostream>
#include <string>

bool fn_created = false;

template <typename T> void fn( T Val )
{
static FirstTime = true;
if ( FirstTime )
{
if ( fn_created )
throw "Function already created using diff type!";
fn_created = true;
FirstTime = false;
}
std::cout << "Value is " << Val << "\n";

}

int main()
{
fn<int>( 10 );
fn<int>( 20 );
fn<int>( 30 );
// Uncommenting out following line will cause run time throw
// fn<double>( 40.0 );

std::string wait;
std::getline( std::cin, wait );
}

Sorry, ignore this. This is runtime. I didn't fully understand your
question until I reread it.
 
A

Alf P. Steinbach

* Dave Rahardja:
Is it possible to prevent _at compile or link time_ the mulitple instantiation
of a templated function? In other words, if there exists a function

template <typename T> void fn();

I want to prevent the user from doing this:

int main()
{
fn<int>();
fn<double>(); // <-- should cause compile time or link time error
}

It matters that there is only one instance of the function in the entire
program. It doesn't matter which type becomes instantiated, but there cannot
be two references to the function with two different type parameters.

With 20-20 hindsight this might seem trivial:

template< typename T >
void fn()
{
COMPILE_TIME_ASSERT( SameType<T, ::FnType>::yes );
// ...
}

where ::FnType must be defined by the client code.

It might be possible to circumvent the restriction by using an anonymous
namespace (say), but then anything can be circumvented -- I gather the
problem is to prevent inadvertent multiple specializations.
 
D

Dave Rahardja

* Dave Rahardja:

With 20-20 hindsight this might seem trivial:

template< typename T >
void fn()
{
COMPILE_TIME_ASSERT( SameType<T, ::FnType>::yes );
// ...
}

where ::FnType must be defined by the client code.

It might be possible to circumvent the restriction by using an anonymous
namespace (say), but then anything can be circumvented -- I gather the
problem is to prevent inadvertent multiple specializations.

I don't understand how your solution works. Could you elaborate on what
SameType<...> does?
 
A

Alf P. Steinbach

* Dave Rahardja:
I don't understand how your solution works. Could you elaborate on what
SameType<...> does?

Checks whether the types are the same. I thought that would be clear.
But then, I'm not the world's greatest communicator...

template< typename T, typename U >
struct SameType { enum { yes = false }; };

template< typename T >
struct SameType<T, T> { enum { yes = true }; }

That leaves COMPILE_TIME_ASSERT and where ::FnType comes from.

The Boost library has a nice COMPILE_TIME_ASSERT.

::FnType must be defined by the client code.

Since it's at namespace scope (I used the global namespace for purpose
of illustration) it can't be duplicated if it's a class. And to make
more sure that it's a class you can let the function refer to a nested
typedef. The client code's definition might then look like

struct FnType { typedef double Type; };

and the function definition as

template< typename T >
void fn()
{
COMPILE_TIME_ASSERT( SameType<T, ::FnType::Type>::yes );
// ...
}

OK? Well, disclaimer: I haven't tried this. But I can't think why it
shouldn't work.
 
G

Greg

Why not require that the client announce their choice for the
specialized type by declaring a typedef named, say, "SelectedType"? The
implementation would then specialize the function template or class
template for this typedef only (and not provide a general template
definition):

// library.h

#include "library_config.h"

template <typename T> void fn();

template <>
void fn<SelectedType>()
{
...
}

the client adds the following to library_config.h:

// library_config.h

typedef int SelectedType;

Here's the client program:

#include "library.h"

int main()
{
fn<SelectedType>(); // OK
fn<int>(); // OK
fn<double>(); // Error: undefined function
}

Note that with this technique, instantiating the "wrong" function
template leads a link time error - while instantiating the "wrong"
class template causes a compile-time error.

Greg
 
O

Old Wolf

Dave said:
Hi all,

Is it possible to prevent _at compile or link time_ the mulitple instantiation
of a templated function? In other words, if there exists a function

template <typename T> void fn();

I want to prevent the user from doing this:

int main()
{
fn<int>();
fn<double>(); // <-- should cause compile time or link time error
}

You could set up a script to parse the linker output and see if
the function has been instantiated twice. This is system-specific
of course :)
 
G

Greg

Old said:
You could set up a script to parse the linker output and see if
the function has been instantiated twice. This is system-specific
of course :)

It might also be possible to schedule twice-monthly, on-site code
inspections of each client installation that uses this function
template - as yet another way to provide an extra margin of safety.
After all, it's hard to top in situ verfication by the very same
engineer or engineers who wrote the library - to determine for sure
whether the client has properly met all of its requirements. Effective?
surely, but practical? not hardly.

In the real world, a well-designed software interface should enforce
its own requirements (and report - with at least a semi-coherent
explanation - when any of its requirements have not been met). A
poorly-designed software interface, in constrast, is often one designed
to suit the convenience and tastes of its implementor. As such, this
kind of interface usually requires the client to absorb a set of
"just-so" knowledge describing the proper use of the software - a set
of requirements that often seem to follow no known existing convention
or practice, be motivated by no discernable rationale or purpose, yet
nonetheless will fail - even silently - unless its inscrutable
requirements and procedures are followed to a tee.

So, if at anytime, it starts to look that a particular software
interface under design is looking more and more like it belongs in the
latter category instead of the former, then a top-design review of the
entire interface is probably in order.

Greg
 
D

Dave Rahardja

I don't understand how your solution works. Could you elaborate on what
Checks whether the types are the same. I thought that would be clear.
But then, I'm not the world's greatest communicator...

template< typename T, typename U >
struct SameType { enum { yes = false }; };

template< typename T >
struct SameType<T, T> { enum { yes = true }; }

That leaves COMPILE_TIME_ASSERT and where ::FnType comes from.

The Boost library has a nice COMPILE_TIME_ASSERT.

::FnType must be defined by the client code.

Since it's at namespace scope (I used the global namespace for purpose
of illustration) it can't be duplicated if it's a class. And to make
more sure that it's a class you can let the function refer to a nested
typedef. The client code's definition might then look like

struct FnType { typedef double Type; };

and the function definition as

template< typename T >
void fn()
{
COMPILE_TIME_ASSERT( SameType<T, ::FnType::Type>::yes );
// ...
}

OK? Well, disclaimer: I haven't tried this. But I can't think why it
shouldn't work.

I see. However this pushes the responsibility for creating the check type
(FnType) to the user of the library, where she has to ensure that every
instantiation of fn() will see the _same_ FnType. Putting something in the
global namespace is also undesirable for my library design. I've been burned
too many times by symbol clashes when two libraries collide.

-dr
 
D

Dave Rahardja

You could set up a script to parse the linker output and see if
the function has been instantiated twice. This is system-specific
of course :)

This is the very situation that I'm trying to prevent: a manual,
build-specific, or team process-based solution. I'm building a generic library
that must work on a wide variety of compilers and target microprocessors, and
I want semantic checks to be as automatic as possible, using standard C++ as
much as possible.

-dr
 
A

Alf P. Steinbach

* Dave Rahardja:
I see. However this pushes the responsibility for creating the check type
(FnType) to the user of the library,

Yes of course. It's the user that uses the template and decides the
template argument, no? Only the user knows that type.

where she has to ensure that every
instantiation of fn() will see the _same_ FnType.

Yes, that's ensured by the one-definition-rule (ODR), paragraph
something of the standard.

Putting something in the
global namespace is also undesirable for my library design. I've been burned
too many times by symbol clashes when two libraries collide.

The use a namespace specific to your library. In C++ namespaces can be
extended.
 

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,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top