I am currently playing on a project using Boost.ProgramOptions and I had to create the following structure to add some constraints on an option:
template <const char *str1, const char*... str2>
struct restrictedValues
{
...
};
In order to validate that new option you must overload the boost::program_options::validate function:
template<class T, class charT>
void validate(boost::any& v, const std::vector< std::basic_string<charT> >& xs, T*, long);
The call for this validate function is the following:
validate(value_store, new_tokens, (T*)0, 0);
As precised by boost: "The target type is specified via a parameter which has the type of pointer to the desired type. This is workaround for compilers without partial template ordering, just like the last 'long/int' parameter."
I, hence, wrote my validate version in the following way:
template<class charT, const char *... str>
void validate(boost::any &v,
const std::vector<std::basic_string<charT> >& values,
restrictedValues<str...>* /*target_type*/,
int /*unused*/) { ... }
It looks like my version of clang (Ubuntu clang version 3.5.0-4ubuntu2~trusty2 (tags/RELEASE_350/final) (based on LLVM 3.5.0)) simply skip my version and gracefully fail in the default version. Whilst my gcc ((Ubuntu 4.8.2-19ubuntu1) 4.8.2) happily compile.
EDIT See a live example that shows the different behaviour, credits #dyp:
Live On Coliru
#include <boost/any.hpp>
#include <vector>
#include <string>
#include <iostream>
template <const char *str1, const char*... str2> struct restrictedValues
{
/*...*/
};
template<class T, class charT>
void validate(boost::any&, const std::vector< std::basic_string<charT> >&, T*, long)
{
std::cout << "default version\n";
}
extern char const client[] = "hello";
extern char const server[] = "world";
template<class charT, const char *... str>
void validate(boost::any &,
const std::vector<std::basic_string<charT> >&,
restrictedValues<str...>* /*target_type*/,
int /*unused*/) {
std::cout << "custom version\n";
}
int main()
{
boost::any a;
std::vector<std::string> xs;
restrictedValues<client, server>* p = 0;
validate(a, xs, p, 0);
}
Moreover the same process using non-variadic templates (a fixed number of const char*) for the structure/function does work like a charm.
I am not quite sure which lookup process leads to such a ambiguous error.
If my function didn't use template, it would be selected according to the overloading rules, but that isn't the case. By reading the partial ordering rules for template functions, both functions have the same specialization for the template parameters, but my expectation would be that the int/long trick should work. Any idea on how to solve this template mystery?
The usual approach here is to make ADL work using strong typedefs.
I've documented this in an older answer¹:
Custom validator for boost program_options doesn't work with GCC, works with MSVC
¹ the first comments there are obsolete and refer to the old answer I had before.
Related
This question already has answers here:
C++ parameter pack, constrained to have instances of a single type?
(7 answers)
C++ Multiple function parameters with varargs of a specific type [duplicate]
(2 answers)
Why must C++ function parameter packs be placeholders or pack expansions?
(3 answers)
Can I create a function which takes any number of arguments of the same type?
(3 answers)
Closed 6 months ago.
I'm writing a utility function in c++ 11 that adds an element of a single type to a vector. Most variable argument docs/guides I've found show a template with the typedef type, but I'm looking to only allow a single type for all the variable arguments (const char*). The following is the relevant snippet of code:
Item.hpp:
// Guard removed
#include <vector>
class Item {
public:
Item(const char* n, bool (*optionChange)(uint8_t), const char*...);
private:
std::vector<const char*> options;
void addOption(const char*);
}
Item.cpp:
#include "Item.hpp"
void Item::addOption(const char* option) {
options.push_back(option);
}
Item::Item(
const char* n,
bool (*optionChange)(uint8_t),
const char* opts...
): name(n), type(MENU_TYPE_OPTS), selectedOpt(0) {
addOption(opts...); // Doesn't compile
}
Compilation of the above code fails with the message error: expansion pattern 'opts' contains no argument packs.
Use a variadic template. With variadic template, also all types can be different, but you can request that they are all the same via SFINAE
#include <type_traits>
#include <tuple>
template <typename ...T>
std::enable_if_t<
std::is_same_v< std::tuple<const char*, T...>,
std::tuple<T...,const char*>>
,void>
foo(T...t) {}
int main() {
const char* x;
int y;
foo(x,x,x,x); // OK
foo(x,y,x); // error
}
This is based on a neat trick to check if all types of a variadic pack are the same type (i'll add the referene to the original when I find it). std::tuple<const char*, T...> and std::tuple<T...,const char*> are only the same type when all Ts are const char*. std::enable_if will discard the specialization when the condition (all Ts are const char*) is not met and attempting to call it results in a compiler error.
This is rather old fashioned and works already in C++11 (apart from the _v/_t helpers). I suppose in C++20 there are less arcane ways to require all Ts to be const char*.
I missed that it is a constructor and you cannot do return-type-SFINAE on a constructor. It just needs to be a little more convoluted:
#include <type_traits>
#include <tuple>
struct foo {
template <typename ...T,
std::enable_if_t<
std::is_same_v< std::tuple<const char*, T...>,
std::tuple<T...,const char*>
>,
bool
> = true>
foo(T...t) {}
};
int main() {
const char* x;
int y;
foo f1(x,x,x,x); // OK
foo f2(x,y,x); // error
}
When the condition is met the last template parameter is non-type bool and has a default value of true. It's only purpose is to fail when the condition is not met (hence it does not need to be named).
You can't expand variadic arguments like parameter packs. You either have to switch to another approach (like the parameter packs/std::initializer_list) or rely on variadic functions of <cstdarg>:
Item::Item(const char* n, bool (*optionChange)(uint8_t), size_t opt_num, ...): name(n), type(MENU_TYPE_OPTS), selectedOpt(0) {
va_list args;
va_start(args, opt_num);
for (decltype(opt_num) i = 0; i < opt_num; ++i) {
const auto option = va_arg(args, const char *);
std::cout << option << std::endl;
}
va_end(args);
...
}
Be advised that in this scenario, you don't have control over types of passed arguments, and the entire contract is supposed to be agreed verbally or in comments. The client code also should provide meta-data for the approach to be viable (i.e. in this example opt_num stores the number of strings passed)
EDIT
CPP community definitely doesn't like the varargs thing, as my answer was instantly downvoted, so another, more C++ friendly solution is to replace varargs with std::initializer_list:
Item::Item(const char* n, bool (*optionChange)(uint8_t), std::initializer_list<const char *> options): name(n), type(MENU_TYPE_OPTS), selectedOpt(0) {
for (const auto& option: options){
std::cout << option << std::endl;
}
...
}
Let's say I have a function:
#include <optional>
template <typename T>
std::optional<T> foo(T const &input);
It accepts a value, attempts to work with a copy of it and returns said copy on success (std::nullopt on fail).
But the problem is, when a string literal is passed into such function, an error T in optional<T> must meet the Cpp17Destructible requirements occurs.
It's caused by static_assert(is_object_v<_Ty> && is_destructible_v<_Ty> && !is_array_v<_Ty>, ...) defined in <optional>.
The next expression works correctly:
foo((char const*) "bar");
This one fails:
foo("bar");
The question is, how do I force the compiler to implicitly convert char const[] to char const*?
P. S. I know that it could be done by simply overloading the function, by I'm not too keen on code duplication it causes, so I'm curious whether an alternative solution is applicable here.
Edit: Rewrote the answer. With return type deduction, this would be convenient.
template <typename T>
auto foo(T const &input){
auto copy {std::move(input)};
// ...
return std::optional{std::move(copy)};
}
Not really what you asked for, though consider that not much repetition is needed:
template <int s>
std::optional<const char*> foo(const char (&str)[s]) {
return foo(&str[0]);
}
or simpler:
std::optional<char const*> foo(char const *input) {
return foo<char const *>(input);
}
Is it possible to have a traits in order to convert, let's say, char* to const char* in order to use it further to call functions having const char* parameters using a char* variable?
I have this (the context is large, I've simplified, sorry for the ugly code):
#include <iostream>
using namespace std;
#include <stdio.h>
#include <string.h>
#include <typeinfo>
template<typename T, typename U, std::enable_if_t<std::is_same<T, U>::value, int> = 0>
T convert_type(U _in)
{
return _in;
}
template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_lvalue_reference_t<U>>::value, int> = 0>
T& convert_type(U& _in)
{
return _in;
}
template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_lvalue_reference_t<std::add_const_t<U>>>::value, int> = 0>
T& convert_type(U& _in)
{
return _in;
}
template<typename T, typename U, std::enable_if_t<std::is_same<T, std::add_pointer_t<U>>::value, int> = 0>
T convert_type(U& _in)
{
return std::addressof(_in);
}
int main() {
char* c = new char[sizeof "test"];
strcpy(c, "test");
const char * cc = convert_type<const char *, char *>(c); // here not compilable yet due to lack of the right convert_type specialization
cout << c;
delete[] c;
return 0;
}
Just to clarify, I embed SpiderMonkey to script Illustrator API. This made me write pretty complicated code, but I have checked and I know the conversion traits above are in use for various function calls. I've added them here just to see the approach and to clarify the need of adding another that recognizes a type and returns the needed type. All your comments are correct generally, but not in my context. I might have a convoluted code, but I have simplified it as much as I could.
sample code here
As far as it concerns me, the question is not yet answered and I couldn't find a solution. I put the question this way: how to write a traits method to match char* against const char*, which is char const*, actually, when I check in msvc with typeid a type or variable declared as const char*?
Is it possible to have a traits in order to convert, let's say, char* to const char* in order to use it further to call functions having const char* parameters using a char* variable?
As pointed out in the comments, a traits conversion isn't needed at all, since a char* pointer can be used equally as a const char* pointer at any time, since the conversion is implicit (same for any other type than char).
Note that the line in your main() function
char * c = "test";
isn't valid c++ syntax. "test" actually is a const char [5] type and you can't assign that to other than a const char* pointer legally.
At least any attempt to write a value to that pointer will be undefined behavior.
Most c++11 compliant compilers will issue a warning on that statement.
As for your edited example now
char* c = new char[sizeof "test"];
strcpy(c, "test");
//const char * cc = convert_type<const char *, char *>(c);
there's no need for using the convert_type() traits function, you can simply write (as also mentioned in the comments):
const char * cc = c;
(To 'defend' OP: yes, I also thought of simply adding const, since const const T is the same as const T, but, on the other hand, I can think of situations, where you have to pass a type trait that does nothing else but this.)
How about:
// ordinary way w/o type aliases
template<typename T>
struct const_qualify
{
typedef const T type;
};
// do this if you have type aliases
template<typename T>
using const_qualify_t = const T;
Note that, if you just want to compare types, we have now remove_const<> / remove_cv<>: http://en.cppreference.com/w/cpp/types/remove_cv
EDIT: actually, we also have std::add_const<>.
Just use assignment:
const char* cc = c;
(with the caveat that char* c = "test" is ill-formed)
To be more explicit, which is never a bad thing, you can use one of the standard C++ casts:
auto cc = static_cast<const char*>(c);
Your first three convert_type() overloads are basically useless - all of them can be trivially substituted by simply using =. And = actually handles more cases than your convert_type() too.
The fourth overload should be substituted in favor of just & (or directly using std::addressof), there's really no need to hide that you're taking the address. Certainly T* x = &y is a lot easier to understand the meaning of than T* x = convert_type<T*>(y).
Eventually, I've made it, using this post:
template<typename T> struct remove_all_const : std::remove_const<T> {};
template<typename T> struct remove_all_const<T*> {
typedef typename remove_all_const<T>::type *type;
};
template<typename T> struct remove_all_const<T * const> {
typedef typename remove_all_const<T>::type *type;
};
template<typename T, typename U, std::enable_if_t<std::is_same<typename remove_all_const<T>::type, typename remove_all_const<U>::type>::value, int> = 0>
T convert_type(U& _in)
{
return _in;
}
A big Thank! to that post solver!
I have a function that is overloaded for many types.
But my current problem is due to that (LWS here : lws) :
#include <iostream>
#include <string>
#include <sstream>
#include <type_traits>
// First version
template<typename T, class = typename std::enable_if<std::is_fundamental<T>::value>::type>
std::string f(const T& x)
{
return std::to_string(x);
}
// Second version
template<typename... T>
std::string f(const std::tuple<T...>& x)
{
return std::to_string(sizeof...(T)); // It's just an example here
}
// Third version
template<typename T, class = typename std::enable_if<!std::is_fundamental<T>::value>::type, class = void>
std::string f(const T& x)
{
std::ostringstream oss;
oss<<x;
return oss.str();
}
// Main
int main(int argc, char* argv[])
{
std::cout<<f(42)<<std::endl;
std::cout<<f(std::string("Hello World"))<<std::endl;
std::cout<<f(std::tuple<int, int, int, int, int, int>(4, 8, 15, 16, 23, 42))<<std::endl;
return 0;
}
My problem is that when we call f() for a std::tuple, the third version is executed and not the second one.
How to solve this problem (a solution would be to allow the third version only for types where << is defined, but I don't know how to do that, and if this is the best way to solve the problem) ?
Your only problem is that you forgot to #include <tuple>, which is shown in the error on LWS. Here's a fixed version that compiles correctly. The second overload is always a better match than the third one since it's more specialized according to partial ordering rules.
If you still want to know how to check for a functions existence that triggers SFINAE if it's not there, check this answer of mine on a question dedicated to that topic. :) It even has an example that pretty much matches what you want.
I'm trying to derive a technique for writing string-algorithms that is truly independent of the underlying type of string.
Background: the prototypes for GetIndexOf and FindOneOf are either overloaded or templated variations on:
int GetIndexOf(const char * pszInner, const char * pszString);
const char * FindOneOf(const char * pszString, const char * pszSetOfChars);
This issue comes up in the following template function:
// return index of, or -1, the first occurrence of any given char in target
template <typename T>
inline int FindIndexOfOneOf(const T * str, const T * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
Objectives:
1. I would like this code to work for CStringT<>, const char *, const wchar_t * (and should be trivial to extend to std::string)
2. I don't want to pass anything by copy (only by const & or const *)
In an attempt to solve these two objectives, I thought I might be able to use a type-selector of sorts to derive the correct interfaces on the fly:
namespace details {
template <typename T>
struct char_type_of
{
// typedef T type; error for invalid types (i.e. anything for which there is not a specialization)
};
template <>
struct char_type_of<const char *>
{
typedef char type;
};
template <>
struct char_type_of<const wchar_t *>
{
typedef wchar_t type;
};
template <>
struct char_type_of<CStringA>
{
typedef CStringA::XCHAR type;
};
template <>
struct char_type_of<CStringW>
{
typedef CStringW::XCHAR type;
};
}
#define CHARTYPEOF(T) typename details::char_type_of<T>::type
Which allows:
template <typename T>
inline int FindIndexOfOneOf(T str, const CHARTYPEOF(T) * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
This should guarantee that the second argument is passed as const *, and should not determine T (rather only the first argument should determine T).
But the problem with this approach is that T, when str is a CStringT<>, is a copy of the CStringT<> rather than a reference to it: hence we have an unnecessary copy.
Trying to rewrite the above as:
template <typename T>
inline int FindIndexOfOneOf(T & str, const CHARTYPEOF(T) * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
Makes it impossible for the compiler (VS2008) to generate a correct instance of FindIndexOfOneOf<> for:
FindIndexOfOneOf(_T("abc"), _T("def"));
error C2893: Failed to specialize function template 'int FindIndexOfOneOf(T &,const details::char_type_of<T>::type *)'
With the following template arguments: 'const char [4]'
This is a generic problem I've had with templates since they were introduced (yes, I'm that old): That it's been essentially impossible to construct a way to handle both old C-style arrays and newer class based entities (perhaps best highlighted by const char [4] vs. CString<> &).
The STL/std library "solved" this issue (if one can really call it solving) by instead using pairs of iterators everywhere instead of a reference to the thing itself. I could go this route, except it sucks IMO, and I don't want to have to litter my code with two-arguments everywhere a single argument properly handled should have been.
Basically, I'm interested in an approach - such as using some sort of stringy_traits - that would allow me to write GetIndexOfOneOf<> (and other similar template functions) where the argument is the string (not a pair of (being, end] arguments), and the template that is then generated be correct based on that string-argument-type (either const * or const CString<> &).
So the Question: How might I write FindIndexOfOneOf<> such that its arguments can be any of the following without ever creating a copy of the underlying arguments:
1. FindIndexOfOneOf(_T("abc"), _T("def"));
2. CString str; FindIndexOfOneOf(str, _T("def"));
3. CString str; FindIndexOfOneOf(T("abc"), str);
3. CString str; FindIndexOfOneOf(str, str);
Related threads to this one that have lead me to this point:
A better way to declare a char-type appropriate CString<>
Templated string literals
Try this.
#include <type_traits>
inline int FindIndexOfOneOf(T& str, const typename char_type_of<typename std::decay<T>::type>::type* pszSearchChars)
The problem is that when you make the first argument a reference type T becomes deduced as:
const char []
but you want
const char*
You can use the following to make this conversion.
std::decay<T>::type
The documentation says.
If is_array<U>::value is true, the modified-type type is remove_extent<U>::type *.
You can use Boost's enable_if and type_traits for this:
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
// Just for convenience
using boost::enable_if;
using boost::disable_if;
using boost::is_same;
// Version for C strings takes param #1 by value
template <typename T>
inline typename enable_if<is_same<T, const char*>, int>::type
FindIndexOfOneOf(T str, const CHARTYPEOF(T) * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
// Version for other types takes param #1 by ref
template <typename T>
inline typename disable_if<is_same<T, const char*>, int>::type
FindIndexOfOneOf(T& str, const CHARTYPEOF(T) * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
You should probably expand the first case to handle both char and wchar_t strings, which you can do using or_ from Boost's MPL library.
I would also recommend making the version that takes a reference take a const reference instead. This just avoids instantiation of 2 separate versions of the code (as it stands, T will be inferred as a const type for const objects, and a non-const type for non-const objects; changing the parameter type to T const& str means T will always be inferred as a non-const type).
Based on your comments about iterators it seems you've not fully considered options you may have. I can't do anything about personal preference, but then again...IMHO it shouldn't be a formidable obstacle to overcome in order to accept a reasonable solution, which should be weighed and balanced technically.
template < typename Iter >
void my_iter_fun(Iter start, Iter end)
{
...
}
template < typename T >
void my_string_interface(T str)
{
my_iter_fun(str.begin(), str.end());
}
template < typename T >
void my_string_interface(T* chars)
{
my_iter_fun(chars, chars + strlen(chars));
}
Alternative to my previous answer, if you don't want to install tr1.
Add the following template specializations to cover the deduced T type when the first argument is a reference.
template<unsigned int N>
struct char_type_of<const wchar_t[N]>
{
typedef wchar_t type;
};
template<unsigned int N>
struct char_type_of<const char[N]>
{
typedef char type;
};