Best practices for forward declaring a template function

E

er

Hello,

Let's begin with a library that defines:

// A_HPP
namespace library{
class A{};
A::value_type foo(const A& a){...}
}
// B_HPP
namespace library{
class B{};
B::value_type foo(const B& b){...}

}

and I want to extend it, but non intrusively i.e. outside of namespace
library. I proceed like this:

template<typename T> struct is_library : mpl::false_{}; and for each X
in {A,B},

// FWD_A_HPP
#include A_HPP
template<> struct is_library<X> : mpl::true_;

// FWD_FOO
namespace extension{

typename lazy_enable_if<is_library<X>,mpl::identity<typename
X::value_type> >::type
foo(const X& x){
return library::foo(x)
}

}

And I then define my own classes :
// C_HPP
namespace extension{
struct C{};
typename C::value_type foo(const C& c){...};
}


The limitation of the above, to use it say in main.cpp, is that I have
to

#include FWD_A_HPP

before

#include FWD_FOO

Correct?

If the number of dependencies is large, keeping track of which files
comes before other becomes tedious so I guess the standard solution is
to add a forwarding declaration inside FWD_FOO,

namespace library{
typename A::value_type foo(const A& a);
}

just before extension::foo. Correct?

The problem is further complicated by the fact that A, B and C, in my
actual problem, have template e.g.
template<typename T> struct A;
template<typename T,typename U> struct B;

but the function signature is always
templat<typename X>
typename X::value_type foo(const X&);

On some compilers (MSVC), the above does not work. I get a compile
error : ambiguous call to overloaded function.

What is the best practice for this kind of problem?
 
A

Alf P. Steinbach

* er:
Let's begin with a library that defines:

// A_HPP
namespace library{
class A{};
A::value_type foo(const A& a){...}
}
// B_HPP
namespace library{
class B{};
B::value_type foo(const B& b){...}

}

value_type is undefined.

Assuming you meant to define on in both class A and B.

and I want to extend it, but non intrusively i.e. outside of namespace
library. I proceed like this:

template<typename T> struct is_library : mpl::false_{};

and for each X in {A,B},

// FWD_A_HPP
#include A_HPP
template<> struct is_library<X> : mpl::true_;

// FWD_FOO
namespace extension{

typename lazy_enable_if<is_library<X>,mpl::identity<typename
X::value_type> >::type
foo(const X& x){
return library::foo(x)
}

}

What's the point?

And I then define my own classes :
// C_HPP
namespace extension{
struct C{};
typename C::value_type foo(const C& c){...};
}

value_type is not defined.

There is no relation to the preceding explanation.

The limitation of the above, to use it say in main.cpp, is that I have
to

#include FWD_A_HPP

before

#include FWD_FOO

Correct?
No.


If the number of dependencies is large, keeping track of which files
comes before other becomes tedious so I guess the standard solution is
to add a forwarding declaration inside FWD_FOO,

namespace library{
typename A::value_type foo(const A& a);
}

just before extension::foo. Correct?

Doesn't make sense so far.


The problem is further complicated by the fact that A, B and C, in my
actual problem, have template e.g.
template<typename T> struct A;
template<typename T,typename U> struct B;

So, now you're saying that all you've described so far has been *different* from
your problem.


but the function signature is always
templat<typename X>
typename X::value_type foo(const X&);

On some compilers (MSVC), the above does not work. I get a compile
error : ambiguous call to overloaded function.

Error on line 42.

What is the best practice for this kind of problem?

Check out the FAQ item on how to post a question about code that Does Not Work.


Cheers & hth.,

- Alf
 
E

er

After reading the FAQ on code that does not work I'm starting from an
minimal example that I actually tested on XCode 3.2 (Mac OS X). But
before I get too carried away could Alf say if Koenif lookup is what
prompted this question
What's the point?

Back to the example:

#ifndef HEADER1_HPP
#define HEADER1_HPP
namespace n1
{
struct A{ typedef double value_type; };
struct B{ typedef double value_type; };
A::value_type foo(const A& a){ return A::value_type(); }
B::value_type foo(const B& b){ return B::value_type(); }
}
#endif
#ifndef HEADER2_HPP
#define HEADER2_HPP
namespace n2
{
template<typename X>
typename X::value_type foo(const X& x){ return n1::foo(x); }
}
#endif

#include "header1.hpp"
#include "header2.hpp"
int main () {
n2::foo(n1::A());
return 0;
}

Build Succeeded. But changing the order of the include files
#include "header2.hpp"
#include "header1.hpp"
Causes the build to fail, which leads to my question : can / how to
modify the code so that in this order, the code compiles?
 
A

Alf P. Steinbach

* er:
After reading the FAQ on code that does not work I'm starting from an
minimal example that I actually tested on XCode 3.2 (Mac OS X). But
before I get too carried away could Alf say if Koenif lookup is what
prompted this question

I just didn't see the point of all the mpl stuff and templates and so on.

Back to the example:

#ifndef HEADER1_HPP
#define HEADER1_HPP
namespace n1
{
struct A{ typedef double value_type; };
struct B{ typedef double value_type; };
A::value_type foo(const A& a){ return A::value_type(); }
B::value_type foo(const B& b){ return B::value_type(); }
}
#endif
#ifndef HEADER2_HPP
#define HEADER2_HPP
namespace n2
{
template<typename X>
typename X::value_type foo(const X& x){ return n1::foo(x); }
}
#endif

#include "header1.hpp"
#include "header2.hpp"
int main () {
n2::foo(n1::A());
return 0;
}

Build Succeeded. But changing the order of the include files
#include "header2.hpp"
#include "header1.hpp"
Causes the build to fail, which leads to my question : can / how to
modify the code so that in this order, the code compiles?

Simply include [header1.hpp] from [header2.hpp].

That makes [header2.hpp] self contained.

You can optimize by introducing a [header1_fwd.hpp] (like e.g. [iosfwd]), but
there's probably no reason to.

If all the templated foo does is to forward a call then you may instead do

namespace n2 {
using n1::foo;
}


Cheers & hth.,

- Alf
 
E

er

Simply include [header1.hpp] from [header2.hpp].

That makes [header2.hpp] self contained.

Yes, but what if instead of header1.hpp I have header1_a.hpp and
header1_b.hpp, for A and B, respectively, and I want to selectively
include them (in actuality I would have many more than N = 2
classes)?
You can optimize by introducing a [header1_fwd.hpp] (like e.g. [iosfwd]), but

Here's iosfwd (for my own understanding): Declares forward references
to several template classes used throughout iostreams.
there's probably no reason to.

I guess you are suggesting to declare forward not just the classes but
their interface (foo). Right?

I was hoping to avoid this, because if M is the number of functions
(in my example M=1) for each class, I have MxN declarations.
If all the templated foo does is to forward a call then you may instead do

   namespace n2 {
       using n1::foo;
   }

I guess this makes the previous issues irrelevant, but for curiosity's
sake they still are.
Cheers & hth.,

- Alf

Yes it helps, thanks!
 

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,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top