I have an issue with using 'nested'(?) templates as return type for my functions. The function declaration should look something like this:
// ### Generate.hpp
// return the template type
// input type is the same for all versions of generate()
template <typename T> T generate(Input in);
A few inline definitions for base types like int
, bool
are provided.
// ### Generate.hpp
template <> inline int generate<int>(Input in) {/*...*/}
Others ( e.g. custom classes) can be implemented separately.
// ### Custom_Class.hpp
#include "Generate.hpp" // can now make use of template
// ### Custom_Class.cpp
template <> custom::Class generate<custom::Class>(Input in) {/*...*/}
The issue arises when I want to use the function like this:
std::vector<int> x = generate<std::vector<int>>(...);
I would need to define something like this:
template <typename U> std::vector<U> generate(Input input); // differentiating via return type?
and the compiler (g++) won't let this fly.
error: call of overloaded ‘generate<std::vector<int>>(Input in)’ is ambiguous
C++ can't differentiate functions with only the return type. I would prefer functions with the form OutputType func(InputType input)
over func (InputType input, OutputType& output)
, but I'm aware that I can solve this specific scenario in the latter for. There i could regularly overload generate()
for stl container classes and such, which would enable the compile to differentiate.
The question finally boils down to if there is any template magic I can invoke to get what I originally wanted.
Thank you for your help.
The specific use-case here is a function to further process tokens generated by a lexer.
template <typename T> std::pair<size_t, std::optional<T>> parse_tokens(const std::vector<Token>& tokens, size_t start = 0);
The functions gets a vector of Tokens (and a start offset) and must attempt to parse the beginning of the token sequenze into an object ot type T
. That object (alongside the nuber of tokens parsed) is than to be returned.
The ideas that if it is 'known' that after the tokens from def [int] some_data =
are followed by a list of ints, the functions could be called like res = parse_tokens<std::vector<int>>(tokens, list_starts_here)
. Having res.second
for the optional value and res.first
as the ammount of parsed tokens, needed to advance the parser index.
I needed a bit of fidling to make it work with the container class (generate<T>::generate()
must be used, not just generate<T>()
, otherwise the compiler can't deal with it). I haven't implemented this solution yet (only did some quick tests), but I'm confident that it can do what I want, how I want it. It should extend well and I can even have the desired functional syntax.
template <typename T>
struct generator;
template <typename T> T generate(std::string in) {
return generator<T>::generate(in);
}
template <>
struct generator<int> {
static int generate(std::string in) { return std::stoi(in); }
};
template <typename T>
struct generator<std::optional<T>> {
static std::optional<T> generate(std::string in)
{
if (in == "nil")
return std::nullopt;
else
return generator<T>::generate(in);
}
};
Thanks, again.
You can use partial specialization. Since functions can't be partially specialized, switch to a class template:
template <typename T>
struct generator;
template <typename T> T generate(Input in) {
return generator<T>::generate(in);
}
template <>
struct generator<int> {
static int generate(Input in) { /*...*/ }
};
template <typename T>
struct generator<std::vector<T>> {
static std::vector<T> generate(Input in) { /*...*/ }
};
// ### Custom_Class.hpp
template <>
struct generator<CustomClass> {
static CustomClass generate(Input in);
};
// ### Custom_Class.cpp
CustomClass generator<CustomClass>::generate(Input in) {
return {};
}