Reputation: 1385
I'm refactoring some Windows code and I stumbled upon a problem I wanted to see if C++ templates could solve. I want to alter the type used by the function and append a suffix to the function's name (identifier). Note the template name doesn't change, but the instantiated name of the function seen by the linker is what changes.
Since C++ templates normally involve creating overloads of a function whose name does not change, I want to additionally change the name of the function seen by the linker. When calling this function, the portion of the function name will be passed along with the other template parameters. Let me start with a classic example which I will build upon:
template <class DataType>
size_t alterMyString(DataType* s)
{
...
}
//somewhere later - call Wide version of function here.
// function signature: size_t alterMyString(WCHAR* s)
alterMyString<WCHAR>(s);
So far so good. Now, let's say I want the generated function to be alterMyStringWide so that, I may even forward declare the final prototype somewhere else and have callers sync up with the templatized implementation without having to know how it was implemented. I thought this sounds cool in theory!
size_t alterMyStringWide(WCHAR* s);
Here's a way one might attempt to code this, although the use of a preprocessor MACRO is clearly wrong as it would occur before template expansion not to mention gluing strings together with ## when I'm not dealing with strings but rather an identifier. This snippet still shows the gist of what I'm trying to do: gluing an identifier together during template expansion.
//obviously won't compile
#define DECLARE_FUNC_NAME(base,suffix) base##suffix
template <class DataType, class FuncSuffix>
size_t DECLARE_FUNC_NAME(alterMyString,FuncSuffix)(DataType* s)
{
...
}
//can I get this signature: size_t alterMyStringWide(WCHAR* s)
// from a template instantiation like this?
alterMyString<WCHAR,Wide>(s);
Is this possible in any C++ [template] standard? I'm coming up empty when I search if something like this is even possible. I'm not a C++ template guru, so forgive me if there is better terminology for what I'm asking.
A workaround I've come up with involves creating an outer function whose name is what I want, but is internally implemented using a template:
//plain old template
template <class DataType>
size_t tpl_alterMyString(DataType* s)
{
...
}
//this works - I can also forward declare this function prototype and callers
// don't need to know or care whether how it was implemented
size_t alterMyStringWide(WCHAR* s)
{
return(tpl_alterMyString<WCHAR>(s));
}
While the code above works as intended, can it be simplified to remove the outer function such that the final function identifier can be parameterized with the rest of the template arguments?
EDIT: For what its worth, this is an exercise for educational purposes only and not part of any production code. Because this question does not fit into the standard paradigm of how C++ templates are typically used (i.e. function overload generators), this is what prompted me to ask the question here on StackOverflow. Hate on me if you must, but I disagree that the question as proposed is necessarily an implication of bad design. :)
With that said, I understand the benefits of overloads and not having to explicitly call out a special version of a function by relying on slightly different names. There exists much literature on this topic spanning decades and I don't disagree with that paradigm.
But, if one thinks outside of that paradigm for a moment, this doesn't invalidate use cases where you might want the generated function to have a dynamic name. Templates are glorified text-substitution mechanisms by their very nature so I don't think my suggestion is crazy or necessarily contrary to a Best Practice.
In my example, the template is an implementation detail, and nothing more. I know ahead of time which instantiations I want to support; I might not want users calling my template implementation directly with template syntax because I might want my functions to support being called from both C and C++ translation units. Another thing I gain is not having to distribute the implementation; and, at the same time I can still leverage the power of C++ templates to automate the creation of these similar functions.
Upvotes: 0
Views: 65
Reputation: 75765
No, it's not possible (1), but more importantly why would you (2)? One of the advantages of templates is that you can effortlessly go from this:
short foos(short a);
int foo(int a);
long fool(long a);
long long fooll(long long a);
To this
template <class T> T foo(T a) { /* ... */ }
and now to this:
auto foo(std::integral auto a) { /* ... */ }
The caller doesn't need to remember what suffix it needs to apply to the function. You just pass it the right parameter and the proper overload is chosen.
Furthermore you hinder generic programming if you use suffix, e.g.:
template <class T>
auto bar(T a)
{
auto res = foo(a);
/*...*/
}
I can't do that if there are 4 different functions foos
foo
fool
and fooll
instead of 4 overloaded functions foo
.
If you really insist you can manually create the desired overloads:
size_t alterMyStringWide(WCHAR* s) { return alterMyString(s); }
I just honestly see this as a source of needlessly confusion.
1) there might be some preprocessor way of doing it, but that is such a bad idea I am not even going to entertain the thought of it
2) Ok, leaving aside the why. Let's say you need to generate some id
s for some symbol names. You can't do that in C++, except with the preprocessor (macros). The best bet right now would be an external C++ code generation tool (which you would have to build yourself).
There is introspection on its way into the standard which depending on how it's gonna shape could do this, but I wouldn't hold my breath for it.
Upvotes: 2
Reputation: 118691
If your goal is to forward-declare the function, you can use extern template
(explicit instantiation declaration) to hide the template implementation from callers. This is only possible/useful if you actually know all the instantiations that will be needed up front; from your example I'm assuming these will be char and wchar.
// forward_declarations.h
template <class DataType> size_t alterMyString(DataType* s);
extern template size_t alterMyString<char>(char* s);
extern template size_t alterMyString<wchar_t>(wchar_t* s);
// main.cpp
#include "forward_declarations.h"
// ...use alterMyString()...
In a different translation unit...
// impl.cpp
#include "forward_declarations.h"
template <class DataType> size_t alterMyString(DataType* s) {
// implementation here
}
template size_t alterMyString<char>(char* s);
template size_t alterMyString<wchar_t>(wchar_t* s);
Upvotes: 0