Generic function template deduction over existing function overloads - c++

I'm writing an extensible library where it has become convenient to overload STL's to_string() for custom types. For that I've designed a generic overload template that throws an exception if not specialized:
namespace std {
// ...
template < typename T >
inline std::string to_string(const T& in, const std::string& separator = ",") {
throw std::runtime_error("invalid call to " + std::string(__func__) + "(): missing template specialization for type " + typeid(T).name());
}
} // namespace std
This is useful mainly because the description will provide a clear explanation on the issue and how to solve it, and avoids having to use polymorphism to implement derived implementations (the function is only marginally/optionally required for certain applications such as serialization, I/O, etc.).
However, the issue with this approach is that the overload template will be deduced even with types where <string> already provides an overload for.
My question is if is there a way to force the non-template overload to be used only when there is no non-template definition available?

I recommend that you do not generate a runtime exception for something that should be a compilation failure.
It could look like this:
#include <string>
#include <type_traits>
namespace extra {
template <class T>
inline std::string to_string(const T& in) {
static_assert(std::is_arithmetic_v<T>, "T needs extra::to_string overload");
return std::to_string(in);
}
} // namespace extra
... and then you don't need to check if it's an arithmetic type at the call site:
template <class T>
void func(const T& arg) {
std::cout << extra::to_string(arg);
}
Demo

I ended up declaring to_string on a different namespace, and made use of type traits to delegate basic types towards STL's std::to_string:
namespace extra {
template < typename T >
struct invalid : std::false_type { /* ... */ };
template < typename T >
inline std::string to_string(const T& in) {
// static_assert(invalid< iT >::value, "Invalid call to extra::to_string(): missing template specialization for required type!"); // never compiles
throw std::runtime_error("Invalid call to extra::_to_string(): missing template specialization for required types[" + std::string(typeid(T).name()) + "]!");
}
} // namespace extra
template < typename T >
void func(const T& arg) {
// ...
if constexpr (std::is_arithmetic< T >()) {
std::cout << std::to_string(arg);
} else {
std::cout << extra::to_string(arg);
}
// ...
}
Although I am still trying to figure out how to proper write the static assertion in order to generate the error during compilation, at this stage this behaves how I needed it to.

Related

Toggle pass by reference parameter based on template type C++

I am implementing a "starts_with" function to check if a string starts with some prefix. I want the function to be able to compare std::string and std::string_view interchangeably. The issue I'm running into is when a std::string is passed as an argument I want it to be passed by reference and a std::string_view to be passed by value.
Currently I have this setup:
#include <string_view>
#include <utility>
template <typename String>
struct string_type {
using type = const String&;
};
template <>
struct string_type<std::string_view> {
using type = const std::string_view;
};
template <typename String>
using string_type_t = typename string_type<String>::type;
template <typename String, typename Prefix>
bool string_starts_with(string_type_t<String> str, string_type_t<Prefix> pre) {
if (pre.length() > str.length()) {
return false;
}
for (auto ch = std::pair{str.begin(), pre.begin()};
ch.second != pre.end();
++ch.first, ++ch.second) {
if (*ch.first != *ch.second) {
return false;
}
}
return true;
}
int main() {
using namespace std::string_view_literals;
return string_starts_with("hello"sv, "hel"sv) ? 0 : 1;
}
However gcc and clang (tested here) are unable to deduce the template parameters and I have to specify the types explicitly string_starts_with<std::string_view, std::string_view>(..., ...).
One obvious solution would be to provide overloads for std::string_view but then I need to implement 4 different functions with essentially the same body (string_starts_with(std::string, std::string), string_starts_with(std::string, std::string_view), string_starts_with(std::string_view, std::string_view), string_starts_with(std::string_view, std::string)). This might still be manageable but what if there is another string-like object such as std::vector<char> or std::array<char> that I want to introduce to the API it just becomes unmanageable.
Nesting a type-aliase disables deduction. Because the compiler has no way to guess which class the nested parameter is possibly defined in. You can define a traits template and/or use meta-programming constructs (enable_if, if constexpr, concept/requires, static_assert...) to constraint the template:
template<typename >
struct str_ref_traits: std::false_type{};
template<>
struct str_ref_traits<std::string&>: std::true_type{};
template<>
struct str_ref_traits<std::string_view>: std::true_type{};
template <typename S, typename P>
bool string_starts_with(S str, P pre) {
static_assert(str_ref_traits<S>::value);
static_assert(str_ref_traits<P>::value);
if (pre.length() > str.length())
return false;
return std::equal(begin(pre), end(pre), begin(str));
};
Of course as mentioned in the comments, std::string_view can handle std::string - by design. But I wanted to mention the general rule that nested type-aliase cannot be deduced.
In fact, the design goal of std::type_identity is to be used as a deduction disabler on purpose.
regards,
FM.

Unable to get `std::enable_if` to work for unscoped enum

I'm trying to build a utility ToString function that either calls std::to_string or a custom to_string method defined somewhere else. This is what I came up with for version 1.0:
Context
The code here is a close approximation to what I'm working with for context. I have enums from a 3rd party library that are defined using the C stlye definition, as well as enums I've defined using the C++ style.
//From my 3rd party library
namespace ThirdPartyNamespace
{
typedef enum
{
UnscopedValue
} Unscoped;
}
//Defined in Scoped.h
namespace MyNamespace
{
enum class Scoped
{
ScopedValue
};
static std::string to_string(Scoped value)
{
return "Enums::Scoped";
}
}
//Defined in Helpers.h, contains to_string methods for the enums in the 3rd party library
namespace Helpers
{
static std::string to_string(ThirdPartyNamespace::Unscoped value)
{
return "Enums::Unscoped";
}
}
Calling Code
ThirdPartyNamespace::Unscoped x = ThirdPartyNamespace::UnscopedValue;
MyNamespace::Scoped y = MyNamespace::Scoped::ScopedValue;
std::cout << Utilities::ToString(x) << std::endl;
std::cout << Utilities::ToString(y) << std::endl;
Utility Code
namespace Utilities
{
template <typename T>
std::string ToString(const T& value)
{
using std::to_string;
return to_string(value);
}
}
This compiles and works for Scoped but writes the integer value for Unscoped. I did some research and it seemed like to fix this I would need to use std::enable_if for my enum types. After some research on how std::enable_if works and SFINAE I came up with what I thought would work:
Utility Code
template<typename T, std::enable_if_t<!std::is_enum<T>::value>* = nullptr>
static std::string ToString(const T& value)
{
using std::to_string;
return to_string(value);
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
static std::string ToString(const T& value)
{
return to_string(value);
}
However, this does not compile. Specifically for Unscoped it gives throws a 'to_string': identifier not found error (I verified that was the error by commenting out the call with Unscoped and it compiled and worked as expected).
My question is, why does the compiler fail to find my custom to_string method?
A bonus question: From my reading I found that ::type* = nullptr "is setting a default value to the template 'type' parameter equal to 'nullptr'"(Source), what exactly does that mean and why would one want a default value here?
You need either
move
std::string to_string(ThirdPartyNamespace::Unscoped value)
into namespace ThirdPartyNamespace to be found thanks to ADL
or, change your ToString to
template <typename T>
std::string ToString(const T& value)
{
using std::to_string;
using Helpers::to_string;
return to_string(value);
}

Using templates in std::conditional to determine function argument types

I want all my saving and loading of data to go through the same functions to reduce the chance of bugs. To do this I used a lot of templates (and much function overloading). It worked, my code is now much cleaner, but I was unable to use const for saving (because it goes through the same functions as the loader does, where the data is kept non-const).
I'd like to use the const correctly, so here is an attempt to get a simple version working, where the data (in this case std::vector) is non-const for std::ifstream, and const otherwise:
#include <iostream>
#include <fstream>
#include <vector>
template <class Foo>
void Overload(const Foo & foo)
{
std::cout << "went to const" << std::endl;
}
template <class Foo>
void Overload(Foo & foo)
{
std::cout << "went to non-const" << std::endl;
}
template <class StreamType, typename... Arguments>
void ReadOrWrite (
/* for 1st argument */ StreamType & filestream,
/* type for 2nd argument */ typename std::conditional<
/* if */ std::is_same<StreamType, std::ifstream>::value,
/* then */ std::vector<Arguments...>,
/* else */ const std::vector <Arguments...>
>::type
/*2nd argument name */ & vector
)
{
Overload(vector);
}
int main ()
{
std::ofstream output_filestream;
std::ifstream intput_filestream;
std::vector<int> vector;
ReadOrWrite(output_filestream, vector);
ReadOrWrite(intput_filestream, vector);
return 0;
}
I know that it will compile/run properly if I edit the function calls to this:
ReadOrWrite<std::ofstream, int>(output_filestream, vector);
ReadOrWrite<std::ifstream, int>(intput_filestream, vector);
But I don't want the user of the function to need to list the types during the function call.
Is there a clean way to do what I'm suggesting?
EDIT
There appears to be a question over the legitimacy of my motive.
I did not explain my motive thoroughly because it's not overly simple (neither is it overly complicated) and I respect the readers' time.
The example I gave was the bare component of what I haven't been able to solve - and the "overload" functions were just included to see if it worked.
However it appears that my lack of explanation has caused confusion, so I will expound:
I have made a small library to handle the general-case saving and loading of data. It successfully allows the users' classes to have simple save/load methods by using the following interface:
class SomeClass
{
public:
template <class StreamType>
void SaveOrLoad(StreamType & filestream)
{
saveload::SaveToOrLoadFromFile(filestream,
data_1_,
data_2_,
/* ..., */
data_n_,
);
}
void SaveToFile (const std::string & filename)
{
std::ofstream output_filestream(filename, std::ios::binary);
// file handling
SaveOrLoad(output_filestream);
}
void LoadFromFile (const std::string & filename)
{
std::ifstream input_filestream(ptf::problem_input_file, std::ios::binary);
// file handling
SaveOrLoad(input_filestream);
}
};
This library handles all fundamental data types, STL containers, and any other containers which use the correct SaveOrLoad(StreamType &) interface, including saving and resizing of all containers. The library has forced all saves and loads to go through the same deterministic functions, and hence has completely removed the potential for bugs involving of a save/load mismatch (unless the user misuses the library's simple interface).
The problem that I have with my library - and hence the reason for my question - is a theoretical one because I have no need for it presently: The SaveToFile method should be able to be const.
The best suggestion would be to provide two separated functions, since reading and writing are two distinct operations, no matter what type you sent in it. For example, someone could be fstream for both input and output. Simply by the type system, you can't know the intent. The decision of reading or writing is usually an intent, and these are rarely embeddable into the type system.
Since saving and loading are distinct operation, it should be distinct functions (possibly sharing code between them)
If you really want a function that do both and switch between the types, then I'd suggest constrain the functions for input or output:
// output streams
template <class StreamType, typename... Arguments,
typename std::enable_if<std::is_base_of<std::ostream, StreamType>::value, int>::type = 0
>
void ReadOrWrite (
StreamType & filestream,
std::vector<Arguments...> const& vector
) {
Overload(vector);
}
// input streams
template <class StreamType, typename... Arguments,
typename std::enable_if<std::is_base_of<std::istream, StreamType>::value, int>::type = 0
>
void ReadOrWrite (
StreamType& inputstream,
std::vector<Arguments...>& vector
) {
Overload(vector);
}
Since the second is more specialized than the first, it will be taken whenever the stream is std::istream and the vector is mutable. Otherwise, the first one is taken.
Live example
Another overload solution could transform ReadOrWrite(), almost as you have written in your question, in an helper function
template <typename ... Args, typename ST>
void ReadOrWrite_helper (ST &, typename std::conditional<
std::is_same<ST, std::ifstream>::value,
std::vector<Args...>,
std::vector<Args...> const>::type vec)
{ Overload(vec); }
adding a overloaded couple of ReadOrWrite() function to select select the Args... and explicit them calling the helper function
template <typename ... Ts>
void ReadOrWrite (std::ifstream & is, std::vector<Ts...> & vec)
{ ReadOrWrite_helper<Ts...>(is, vec); }
template <typename ... Ts>
void ReadOrWrite (std::ofstream & is, std::vector<Ts...> const & vec)
{ ReadOrWrite_helper<Ts...>(is, vec); }
Observe that, given that the Args... types are in non deduced context so are to explicated, I've placed they, in the ReadOnWrite_helper() template parameter declaration, before ST; so there is no need to explicit also ST.
Observe also that if you don't need to know the Args... types inside ReadOrWrite_helper(), all became simpler
template <typename V, typename ST>
void ReadOrWrite_helper (ST &, V & vec)
{ Overload(vec); }
template <typename V>
void ReadOrWrite (std::ifstream & is, V & vec)
{ ReadOrWrite_helper(is, vec); }
template <typename V>
void ReadOrWrite (std::ofstream & is, V const & vec)
{ ReadOrWrite_helper(is, vec); }
and also disappear the needs of explicating the V type.
Firstly, apologies, my explanation of my problem was bad and I'll do better in future.
Just in case anyone happens to read this and have a similar problem, I solved it by introducing two wrapper functions whose purpose is to explicitly state the template parameters:
template <class DataType>
void ParseData (std::ofstream & output_filestream, const DataType & data)
{
ReadOrWrite<std::ofstream, DataType> (output_filestream, data);
}
template <class DataType>
void ParseData (std::ifstream & input_filestream, DataType & data)
{
ReadOrWrite<std::ifstream, DataType> (input_filestream, data);
}
The important part is that this solution is scaleable: To handle each new data type I only need to write one ReadOrWrite function, with no unnecessary template parameters at function calls.
How the ParseData functions fit into the solution:
#include <iostream>
#include <fstream>
#include <vector>
// lots of useful typetraits:
#include <type_traits>
template <class S, class T = void>
struct is_vector : std::false_type {};
template <class S, class T>
struct is_vector <std::vector<S,T>> : std::true_type {};
template <typename Datatype>
inline constexpr bool is_vector_v = is_vector<Datatype>::value;
// Kinda similar format to my serialization library:
template <class Foo>
void Overload(const Foo & foo)
{
std::cout << "went to const" << std::endl;
}
template <class Foo>
void Overload(Foo & foo)
{
std::cout << "went to non-const" << std::endl;
}
// Special type trait specific to this library
// (within an anonymous namespace)
template <typename, typename Data>
struct vet_data_constness
: std::add_const<Data> {};
template <typename Data>
struct vet_data_constness <std::ifstream, Data>
: std::remove_const<Data> {};
template <typename StreamType, typename Data>
using vet_data_constness_t
= typename vet_data_constness<StreamType,Data>::type;
template <class StreamType, typename DataType>
std::enable_if_t<is_vector_v<DataType>>
ReadOrWrite (StreamType & filestream,
vet_data_constness_t<StreamType, DataType> & vector)
{
Overload(vector);
}
// These functions are simply for routing back to the ReadOrWrite
// funtions with explicit calls
template <class DataType>
void ParseData (std::ofstream & output_filestream, const DataType & data)
{
ReadOrWrite<std::ofstream, DataType> (output_filestream, data);
}
template <class DataType>
void ParseData (std::ifstream & input_filestream, DataType & data)
{
ReadOrWrite<std::ifstream, DataType> (input_filestream, data);
}
int main ()
{
std::ofstream output_filestream;
std::ifstream intput_filestream;
std::vector<int> vector;
ParseData(output_filestream, vector);
ParseData(intput_filestream, vector);
return 0;
}

Template specialization with enable_if fails in Clang, works with GCC

I am trying to remove a member function based on the template type. The problem is to make a later template specialization match the type signature of my function in a case when it is not removed.
I tried the following code, which compiles with GCC (9.0.1) but gives an error in Clang (9.0.0). I think it also fails to build the code in MSVC++.
#include <type_traits>
#include <iostream>
template <typename T>
struct my_type {
template <typename Q = T>
std::enable_if_t<!std::is_same<bool, Q>::value, my_type<T>> my_fun(const my_type<T>& v) {
std::cout << "Base";
return v;
}
};
template <>
template <typename Q>
std::enable_if_t<!std::is_same<bool, double>::value, my_type<double>> my_type<double>::my_fun(const my_type<double>& v) {
std::cout << "Specialized";
return v;
}
int main()
{
my_type<double> aa, bb;
aa.my_fun(bb);
}
The error with Clang is
prog.cc:16:88: error: out-of-line definition of 'my_fun' does not match any declaration in 'my_type<double>'
std::enable_if_t<!std::is_same<bool, double>::value, my_type<double>> my_type<double>::my_fun(const my_type<double>& v) {
^~~~~~
1 error generated.
I would like to know how to make the code work, and also why the results are not consistent cross all the major compilers.
In both cases: my_type is specialised to double. Then compare non-specialised version of my_fun
template < >
template <typename Q>
std::enable_if_t<!std::is_same_v<bool, Q>::value, my_type<double>>
// ^ (!)
my_type<double>::my_fun(const my_type<double>& v)
against the fully specialised my_fun:
template < >
template < >
// ^
std::enable_if_t<!std::is_same<bool, double>::value, my_type<double>>
my_type<double>::my_fun<double>(const my_type<double>& v)
// ^
Both of above variants would be legal; you, in contrast, ended up somewhere in between...
GCC accepting this code doesn't look right to me, I join the 'this is a bug' fraction in the comments.
Perhaps even worse: Consider my_type<double>::my_fun<bool> specialisation – it should still exist, shouldn't it?
I don't know how to make this work with specialization. But I do know how to just side-step the issue entirely:
template <typename> struct tag { };
template <typename Q = T>
std::enable_if_t<!std::is_same_v<bool, Q>, my_type<T>> my_fun(const my_type<T>& v) {
return my_fun_impl(v, tag<Q>{});
}
with:
template <typename U>
my_type my_fun_impl(const my_type& v, tag<U>) {
std::cout << "Base";
return v;
}
my_type my_fun_impl(const my_type& v, tag<double>) {
std::cout << "Specialized";
return v;
}
If you wanted specialization to give users the ability to add specialized implementations, you could make my_fun_impl a free function instead of a member function. If the goal was just to specialize for certain types, you can make them private member functions.
You cannot use enable_if here to suppress a member function depending on the template parameter of the class, i.e. T (but only depending on the template parameter of the function, i.e. Q.
Your code is wrong, as clang rightly points out. I don't know why gcc accepts it and how it can detect what Q is in your 'specialisation' (I reckon your code compiled with gcc stated "Base" -- correct? Also as there is no inheritance, it's not clear why you use "Base".)
W/o tag type, you could do the following.
template <typename T>
struct my_type {
private:
template<bool Standard>
std::enable_if_t<Base, my_type> my_fun_impl(const my_type& v)
{
std::cout << "Standard";
return v;
}
template<bool Standard>
std::enable_if_t<!Standard, my_type> my_fun_impl(const my_type& v)
{
std::cout << "Specialised";
return v;
}
public:
my_type my_fun(const my_type& v)
{
return my_fun_impl<is_standard<T>::value>(v);
}
};
for whatever is_standard<> you want.

template overload resolution trouble

Given this code:
#include <string>
#include <vector>
#include <iostream>
template <typename T>
std::string stringify(const T&) {
return "{?}";
}
template <typename T>
std::string proxy(const T& in) {
return stringify(in);
}
// trying to specialize "stringify()"
template <typename T>
std::string stringify(const std::vector<T>& in) {
return "vector specialization!";
}
template <>
std::string stringify(const std::vector<int>& in) {
return "INT vector specialization!";
}
int main() {
std::cout << proxy(1); // calls the 1st
std::vector<int> intVec;
std::cout << proxy(intVec); // calls the 1st
std::vector<double> dblVec;
std::cout << proxy(dblVec); // calls the 1st
return 0;
}
How can I specialize stringify() for vector<> after proxy<>?
Currently I get {?}{?}{?}
If I delete this one - stringify(const std::vector<T>& in) then the vector<int> starts getting called because it would be a specialization of the first.
Then I would get {?}INT vector specialization!{?}
Is there any way to call any of the 2 vector specialization stringification functions from proxy() - if they are defined last - after the proxy() function?
Is there a way to partially specialize on vector<> and still get called from proxy<>?
I don't want to specialize for vector<int>, vector<double>, vector<UserType>...
EDIT: forgot to mention I need this for C++98
First of all, avoid specializing function templates, prefer overloading. See Herb Sutter's article on the potential pitfalls.
Secondly, the issue you're running into involves how name lookup works for dependent names in function templates. Inside proxy<T>, stringify is a dependent name - it depends on T. That name will be looked up at the point of definition of the template (which will find stringify<T>(const T&) and not the other overload) and again at the point of instantiation in the associated namespace of the arguments (which would be std). Neither of those lookups find your other functions.
It's that second part of lookup - the argument-dependent lookup - that we can take advantage of. Let's just stick everything in one namespace (which I'm naming N arbitrarily, feel free to rename as appropriate):
namespace N {
struct helper { };
template <typename T>
std::string stringify(helper, const T&) {
return "{?}";
}
}
template <typename T>
std::string proxy(const T& in) {
return stringify(N::helper(), in);
}
Ok, so far we've changed absolutely nothing. We still get {?} in all cases. But the now we can stick further overloads (not specializations) of stringify still in that namespace but after the definition of proxy:
namespace N {
template <typename T>
std::string stringify(helper, const std::vector<T>& ) {
return "vector overload!";
}
std::string stringify(helper, const std::vector<int>& ) {
return "INT vector overload!";
}
}
Those two overloads will be found by the second phase of the name lookup because N is an associated namespace of helper. Now proxy(intVFec) will find all three overloads of stringify instead of just the one. And now your code prints:
{?}INT vector overload!vector overload!
as desired. None of the above requires C++11.