guini
guini

Reputation: 760

multiple functions with same name but different argument types as template parameter

I'm using xsd to create c++ code from a xml schema file. For a xml type multiple functions are created (for serialization etc).
If the type is called XmlType multiple functions of the following form are created:

XmlType XmlType_(const XmlType& a, const string& b)
string XmlType_(const XmlType& a)
...

This are normal functions and not members of XmlType and they all have the same name. For XmlType2 the functions would be called XmlType2_.

I would like to write a utility template class for all the different xml types of my xml scheme. The different functions are going to be called insight this class. What I have so far is something like this:

template<typename T>
using TFunc1 = T (*)(const T&, const string&);
template<typename T>
using TFunc2 = string (*)(const T&);

template<typename T, TFunc1<T> func2, TFunc2<T> func2>
class XmlUtil {
... 
}; 

When create an instance of the XmlUtil class if have to do it like this:

XmlUtil<XmlType, XmlType_, XmlType_> util; 

This feels a bit redundant and gets worse, when I have to pass more functions as parameters.

I would like to use the util class like this:

XmlUtil<XmlType, XmlType_> util; 

or even better like this

XmlUtil<XmlType> util; 

The only way I can think of is to somehow use define, but it doesn't feel right.
Is there an other way to do this?

EDIT: I'm using a define now:

#define TRPL(name) name, name ## _, name ## _
...
XmlUtil<TRPL(XmlType)> util;

I'll edit this, if I find something better (maybe override sets like Yakk suggested in his answer).

Upvotes: 5

Views: 4458

Answers (5)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275395

This looks like a job for override sets.

static struct foo_override_set_type {
  template<typename... Args>
  auto operator()( Args...&& args ) const
  ->
    decltype( foo( std::forward<Args>(args)... ) )
  {  return ( foo( std::forward<Args>(args)... ) ); }
  template<typename T>
  operator T() { return foo; }
} foo_override_set;

Objects of type foo_override_set_type represent the entire override set of foo. Calling them with operator() does an override set lookup on foo and calls the resulting function. Casting them to a function pointer does the same thing as casting the token foo to a function pointer (or other value).

Your code generation can auto-generate such override set types. It can also make a traits class that maps from your type XmlType to the override set of XmlType_ functions via specialization.

Then, your XmlUtil<XmlType> can access the override set of XmlType_ via that traits class. It first instantiates the override set variable, then invokes () on it.

As an aside, @Xeo has a proposal to make creating such objects as easy as typing []XmlType_ in C++1y or C++1z.

Upvotes: 1

Andrew Tomazos
Andrew Tomazos

Reputation: 68648

This:

XmlUtil<XmlType> util;

is impossible because there is no way to get from XmlType to XmlType_. Their relationship is discarded after the automatic code generator.

However this:

XmlUtil<XmlType_> util;

may be possible. You can deduce the function type of XmlType_ and then use the deduced return type which will be XmlType. I believe there are standard library function for this purpose.

As for the two different overloads, that may be trickier. I do not think that you can pass a function overload set as a template parameter, the resolution is done on the template argument in the context of the template parameter to one function. I don't think there is a way to defer this action without using the preprocessor.

So I would argue that you should use a #define. It is better than nothing.

Upvotes: 3

I am not sure I fully understand what you are asking. The common approach for serialization and deserialization would be to create a factory (abstract factory) and resolve the construction of the objects dynamically. Note that this can be improved for complex structures, where the code generator can create member functions to extract the exact type of each one of the members.

But again, I don't fully understand what you are really trying to do... As a recommendation I believe it would help if you provided more of a description of the problem to solve, as the question focuses on how to make your solution work, and that implicitly discards other approaches that might be better designs.

Upvotes: 0

Stefano Falasca
Stefano Falasca

Reputation: 9097

You can use a trait class like this

template <typename T>
struct Trait{
    typedef T type;
    typedef T (*func1)(const T&, const string&);
    typedef string (*func2)(const T&);
};

and make the class XmlUtil have one template parameter (let's name it Trait) and use Trait::type, Trait::func1 and Trait::func2. See here for full usage.

In the example, the type of XmlUtil goes like:

XmlUtil<Trait<XmlType> >

I've done it this way since I don't know well your problem. It might be the case that you can just define the Trait class right into XmlUtil and use

 XmlUtil<XmlType>

Other variations are possible, it just depend on what you need.

You can read a very brief introduction to trait classes here. If you want to read more about this topic I suggest you Modern C++ (Alexandrescu).

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409176

Default template arguments in the class definition?

Like

template<typename T, TFunc1<T> func1 = XmlType_, TFunc2<T> func2 = XmlType_>
class XmlUtil {
    // ...
};

Upvotes: 0

Related Questions