Sunday, October 14, 2007

The template< namespace> construct

2 days ago, I've discovered an interesting technique, which is equivalent to the
template< namespace> construct.

I will first show you an example, because talking about concepts combined with templates is kind of rough ;)

The need

I have a function 'write' - that can be overloaded for different types.
I have 3 namespaces in which this resides - and on each namespace, the meaning of 'write' is completely different:
  • prepend - prepends a string
  • append - appends a string
  • modify - modifies a string
I have several functors which get the namespace as template argument, and then call the write function. So what I want is something like this:

template< namespace convert = prepend>
struct write_time {

template
void operator()(const msg_type & msg) {

...
convert::write(msg, src);
}
};

Why template< namespace> ?

Of course, it would be easy to have the same concept as a template< class>. However, I want other libs/client code to be able to overload my function, and my functor classes to still have the same default (convert_format::prepend).

The construct

The solutions is simple: just defer to a class which forwards to your namespace:

namespace convert_format {
namespace prepend {
void write(const string_type & src, string & dest) {
dest.insert( dest.begin(), src.begin(), src.end() );
}
template< class string> void write(const string_type & src, cache_string_one_str<> & dest) {
dest.prepend_string(src);
}
...
}}

struct do_convert_format {
struct prepend {
template< class string>
void write(const string_type & src, string & dest) {
convert_format::prepend::write(src, dest);
}
};

struct append { ... };
struct modify { ... };
};


When implementing my functor classes, I just use:

template< class convert = do_convert_format::prepend>
struct write_time {
... };


I've used this in my Boost Logging Lib v2 : here.

I'm sure someone has used this before. Is it documented anywhere else?

6 comments:

Michael Safyan said...

Is this portable?

John Torjo said...

Of course it's portable :) Do you see any reason it wouldn't be?

Michael Safyan said...

I meant to ask whether most compilers support the "template<namespace ...>" form. Your "template<class ...>" solution, as you pointed out, is obviously portable.

John Torjo said...

The language does not allow
template < namespace > construct.

That's why I invented that workaround ;)

RafaƂ said...

You can use ADL o guide compiler. You need additional parameter to distingush each version of write funtion.

namespace convert_format {
typedef std::string string_type;
namespace prepend {
class ns {};
void
write(string_type& src, string_type& dst, ns=ns()) {
dst.insert(dst.begin(), src.begin(), src.end());
}
....

////
template < class NS = convert_format::prepend::ns >
struct write_time {
template < class string_type>
void execute(string_type& dst, time_t t) {
std::string src = boost::lexical_cast< std::string >(t);
//ADL - no need to qualify the function
write(src, dst, NS());
}
};
///
...
write_time<>().execute(s, time(0));
write_time< custom_format::append::ns >().execute(s, time(0));

No need for forwarding classes. It works for regular functions but should be verified for function templates (overloading could not work because of preference of regular functions).
Tag/class ns represent namespace and can be passed over as parameter . The cost is additional parameter. Assuming it is provided with default value the usage of such function can be simplified in cases when there is no need to pass ns. On the other hand it could be a problem when we really need some additional default parameters.

John Torjo said...

Well, that is interesting.
I guess indeed it could remove the need for forwarding classes.

I need to think about it :)

Best,
John