Is there any way to get behavior like this?
// Some definition(s) of operator "" _my_str
// Some definition of function or macro MY_STR_LEN
using T1 = MY_STR_LEN("ape"_my_str);
// T1 is std::integral_constant<std::size_t, 3U>.
using T2 = MY_STR_LEN("aardvark"_my_str);
// T2 is std::integral_constant<std::size_t, 8U>.
It seems not, since the string literals are passed immediately to some_return_type operator "" _my_str(const char*, std::size_t); and never to a literal operator template (2.14.8/5). That size function parameter can't be used as a template argument, even though it will almost always be a constant expression.
But it seems like there ought to be some way to do this.
Update: The accepted answer, that this is not possible without an extra definition per literal, is accurate for C++11 as asked, and also C++14 and C++17. C++20 allows the exact result asked for:
#include <cstdlib>
#include <type_traits>
#include <string_view>
struct cexpr_str {
const char* ptr;
std::size_t len;
template <std::size_t Len>
constexpr cexpr_str(const char (&str)[Len]) noexcept
: ptr(str), len(Len) {}
};
// Essentially the same as
// std::literals::string_view_literals::operator""sv :
template <cexpr_str Str>
constexpr std::string_view operator "" _my_str () noexcept
{
return std::string_view(Str.ptr, Str.len);
}
#define MY_STR_LEN(sv) \
std::integral_constant<std::size_t, (sv).size()>
Reading C++11 2.14.8 carefully reveals that the "literal operator template" is only considered for numeric literals, but not for string and character literals.
However, the following approach seems to give you constexpr access to the string length (but not the pointer):
struct MyStr
{
char const * str;
unsigned int len;
constexpr MyStr(char const * p, unsigned int n) : str(p), len(n) {}
};
constexpr MyStr operator "" _xyz (char const * s, unsigned int len)
{
return MyStr(s, len);
}
constexpr auto s = "Hello"_xyz;
Test:
#include <array>
using atype = std::array<int, s.len>; // OK
Related
I'm trying to create a struct template with a variadic template type pack, that can deduct the sum of the size of all types passed in.
Below you find a simplified example, in the real-world context, the size computed is used to create further member objects.
template <typename... Types>
struct OverallSize
{
template <typename FirstType, typename... NextTypes>
static constexpr size_t sizesum() { return sizeof (FirstType) + sizesum<NextTypes...>(); }
template <typename LastType>
static constexpr size_t sizesum() { return sizeof (LastType); }
static constexpr size_t size = sizesum<Types...>();
};
// Should work e.g. like this
auto s = OverallSize<int, float, char>::size; // s will be 9 on x86-64
I'm used to this recursive parameter unpacking approach when it comes to argument lists and assumed this works as well with argument-less functions and explicit template specification. However I get the following error when compiling with clang
Call to 'sizesum' is ambiguous
...
Candidate function [with FirstType = unsigned long, NextTypes = <>]
Candidate function [with LastType = unsigned long]
So it seems as if the last recursion iteration doesn't work here – not sure why the compiler doesn't simply chose the most obvious choice: The one with only one template type – just as it would happen if there was an actual template argument passed to the function.
So, what do I have to do to make this compile and work as desired?
For C++14 you can use SFINAE:
template <
typename FirstType,
typename... NextTypes,
std::enable_if_t<sizeof...(NextTypes) >= 1>* = nullptr >
static constexpr size_t sizesum() {
return sizeof (FirstType) + sizesum<NextTypes...>();
}
this template will be considered only if parameters pack has size >= 1.
Demo
I am playing with some piece of code, taken from Avoid if-else branching in string to type dispatching answer from Vittorio Romeo, but rewritten to use with C++14 cause Vittorios version uses C++17 fold expressions. I also thought the rewrite would be a good exercise.
Here is the code:
#include <type_traits>
#include <iostream>
#include <utility>
#include <string>
template<char... Cs>
using ct_str = std::integer_sequence<char, Cs...>;
template<typename T, T... Cs>
constexpr ct_str<Cs...> operator""_cs() { return {}; }
template<typename Name, typename T>
struct named_type
{
using name = Name;
using type = T;
};
template<typename... Ts>
struct named_type_list { };
using my_types = named_type_list<
named_type<decltype("int"_cs), int>,
named_type<decltype("bool"_cs), bool>,
named_type<decltype("long"_cs), long>,
named_type<decltype("float"_cs), float>,
named_type<decltype("double"_cs), double>,
named_type<decltype("string"_cs), std::string>
>;
template<std::size_t... Is, char... Cs>
constexpr bool same_impl(const std::string& s,
std::integer_sequence<char, Cs...>,
std::index_sequence<Is...>)
{
const char c_arr[] = {Cs...};
for (std::size_t i = 0; i != sizeof...(Cs); ++i) {
if (s[i] != c_arr[i]) return false;
}
return true;
//Original C++17 (fold expression)
//return ((s[Is] == Cs) && ...);
}
template<char... Cs>
constexpr bool same(const std::string& s, std::integer_sequence<char, Cs...> seq)
{
std::cout << "checking '" << s << "' against '";
std::initializer_list<bool>{ bool(std::cout << Cs)... };
std::cout << "'\n";
return s.size() >= sizeof...(Cs)
&& same_impl(s, seq, std::make_index_sequence<sizeof...(Cs)>{});
}
template<typename... Ts, typename F>
void handle(named_type_list<Ts...>, const std::string& input, F&& f)
{
using expand_type = int[];
expand_type{ 0, (same(input, typename Ts::name{}) && (f(Ts{}), false), 0)... };
//(void)std::initializer_list<int> {
// ( (same(input, typename Ts::name{}) && (f(Ts{}), false) ), 0)...
//};
//Original C++17 (fold expression)
//( (same(input, typename Ts::name{}) && (f(Ts{}), true) ) || ...);
}
int main(int argc, char** argv)
{
const std::string input{"float"};
handle(my_types{}, input, [](auto t)
{
std::cout << typeid(typename decltype(t)::type).name() << "\n";
// TEST: define std::vector with value_type (matched type "float") and add a few values
using mtype = typename decltype(t)::type;
std::vector<mtype> x;
x.push_back(2.2); // <-- does not compile
});
return 0;
}
I assume problem lies in the handle function that seems not to stop the evaluation properly. It should stop at the first invocation of f() in case of a match. Instead, it executes f() in case of a match as expected, but continues executing the remaining types in the named_type_list.
The current code results in this output:
checking 'float' against 'int'
checking 'float' against 'bool'
checking 'float' against 'long'
checking 'float' against 'float'
f
checking 'float' against 'double'
checking 'float' against 'string'
Actually I have no clue how to get that fixed. I tried to rewrite the C++17 fold expression using the std::initializer_list trick and also tried to use an expander (the uncommented part in the handle body. So I guess it is the expression itself not working properly.
Unfortunately I am out of ideas whats really happening at this point, also the fact that I am not experienced with Meta-Programming/Compile-time evaluation.
Another problem arises with an possible use of this code:
My use case would be in an XML property reader where I have type/value tags, e.g. <attribute type="double" value="2.5"/>, applying something like the handle function to get the typename from the type attribute value. That type I could use to further process the value.
For this I added within the handle f()-body in main() 3 lines, defining an std::vector with the found type and trying to add a value to it. This code does not compile, g++ responds with
error: no matching function for call to ‘std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >::push_back(double)’
I guess this is the mixup out of compile-time and run-time behaviour and it does not work this way and that makes me curious how I could further process/use the matched type.
Thanks for your time on explanation, any help is greatly appreciated!
||, of course, short-circuits. Your version doesn't. I don't see short-circuiting as essential to correctness here, but if you want, it's easily implemented with an additional bool:
bool found = false;
expand_type{ 0, (!found &&
same(input, typename Ts::name{}) &&
(f(Ts{}), found = true), 0)... };
The second problem is because your handler function must be validly callable for every possible type in the type list, but you can't push_back 2.2 into a std::vector<std::string>. As an example, you might have something that obtains the value as a string, and the handler body could lexical_cast it to mtype.
This looks like an issue in clang (I've already opened a bug here), but I'd like to be sure that I'm not doing a mistake.
Consider the following code:
#include <type_traits>
#include <cstddef>
template<std::size_t N, std::size_t M, std::enable_if_t<not (N>M)>* = nullptr> // (1)
struct S: public S<N+1, M> { };
template<std::size_t N>
struct S<N, N> { };
int main() {
S<0, 1> c{};
}
It fails to compile with the following error:
8 : error: non-type template argument specializes a template parameter with dependent type 'std::enable_if_t M)> *' (aka 'typename enable_if M), void>::type *')
struct S { };
The same code works as expected using the following line instead of (1):
template<std::size_t N, std::size_t M, typename = std::enable_if_t<not (N>M)>>
The SFINAE expression is almost the same. It is based on a specialization of std::enable_if_t and I would expect the same result (success or failure) for both of the examples.
Are my expectations wrong?
Note that GCC works fine in either cases.
I think this is a gcc bug actually, as a result of [temp.class.spec]:
The type of a template parameter corresponding to a specialized
non-type argument shall not be dependent on a parameter of the
specialization. [ Example:
template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error
template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error
—end example ]
In your example, the type of the 3rd template parameter is dependent on a parameter. When you swap it to typename = std::enable_if_t<...>, then this rule no longer applies.
Note: is there any reason to use SFINAE here anyway, as opposed to static_assert-ing?
This question relates to an earlier one I asked regarding implementing something akin to Qt's signal/slots in C++11.
Consider the following (very simplified signal dispatcher, that in this example does nothing of any use, it's just to demonstrate the pattern/problem):
template< typename... TYPES >
class Signal
{
public:
Signal() = default;
~Signal() = default;
template< typename... PARAMETERS >
void broadcast( PARAMETERS &&... p )
{
// static_assert to confirm PARAMETERS can map to TYPES
}
};
This works well enough, but there's some unwanted type conversion going on in practice. e.g.;
// acceptable use.
Signal< int, unsigned, float, char >().broadcast( 1, 2u, 0.f, 'a' );
// should fail compilation, first parameter is a float, 4th is an int.
Signal< int, unsigned, float, char >().broadcast( 0.f, 0, 0.f, 0 );
// acceptable use, first parameter is const, but it's convertible.
const int i = 3;
Signal< int, unsigned, float, char >().broadcast( i, 2u, 0.f, 'a');
// acceptable use, first parameter is const &, but it's convertible.
const int & j = i;
Signal< int, unsigned, float, char >().broadcast( j, 2u, 0.f, 'a');
There should be no silent float to int conversion. Conversion of const/const & in this instance should be possible (the format of TYPES should not have const or & as all data should be passed by value).
I'd like to prevent compilation where such unwanted type conversion happens. I thought to wrap up both TYPES and PARAMETERS in tuples, iterate over the tuple and confirm that each type in a given tuple parameter index matches (including using std::decay), but then I couldn't see a way to do that at compile time so that it could go in a static_assert.
For reference, compilers of choice are clang (latest on OS X 7.3 (clang-703.0.31)) and vc14.
Is what I want to do possible and, if so, can anyone offer any pointers?
Using (once again) the all_true bool pack trick from Columbo:
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
template <class... Args>
struct Signal {
template <class... Dargs, class = typename std::enable_if<all_true<
std::is_same<Args, typename std::decay<Dargs>::type>{}...
>{}>::type>
void broadcast(Dargs &&...) {}
};
This SFINAE's away the function if the parameters don't match exactly.
Here is a metaprogram I quickly came up with. It is a bit coarse, but can be implemented in a more better way. You should probably use the decayed type (std::decay) in the metaprogram to get correct result.
#include <iostream>
#include <type_traits>
template <typename... T> struct param_pack {};
template <typename, typename> struct is_all_same_impl;
template <>
struct is_all_same_impl<param_pack<>, param_pack<>>
{
static bool const value = true;
};
template <typename T, typename S, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<S, SRest...>>
{
static bool const value = false;
};
template <typename T, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<T, SRest...>>
{
static bool const value = is_all_same_impl<param_pack<Rest...>, param_pack<SRest...>>::value;
};
template <typename, typename>
struct is_all_same;
template <typename... FSet, typename... SSet>
struct is_all_same<param_pack<FSet...>, param_pack<SSet...>>: is_all_same_impl<param_pack<FSet...>, param_pack<SSet...>> {};
int main() {
std::cout << is_all_same<param_pack<int, char, float>, param_pack<int, char, int>>::value << std::endl;
return 0;
}
UPDATE :: More simpler version
template <typename... T> struct param_pack {};
int main() {
std::cout << std::is_same<param_pack<int, float, int>, param_pack<int,float,int>>::value << std::endl;
return 0;
}
So you can do something like:
static_assert( is_same<param_pack<Args...>, param_pack<std::decay_t<Dargs>...>>::value, "Parameters do not sufficiently match." );
I'm having trouble with std::initializer_list. I reduced it down to a simple example:
#include <initializer_list>
#include <cstdio>
class Test {
public:
template <typename type> Test(const std::initializer_list<type>& args) {}
};
int main(int argc, char* argv[]) {
Test({1,2});
getchar();
return 0;
}
When compiled using g++ test_initializer.cpp -std=c++0x, it compiles and runs well. However, if line 11 is changed to Test({1,2.0});, one gets:
ian#<host>:~/Desktop$ g++ test_initializer.cpp -std=c++0x
test_initializer.cpp: In function ‘int main(int, char**)’:
test_initializer.cpp:11:14: error: no matching function for call to ‘Test::Test(<brace-enclosed initializer list>)’
test_initializer.cpp:11:14: note: candidates are:
test_initializer.cpp:7:28: note: template<class type> Test::Test(const std::initializer_list<_Tp>&)
test_initializer.cpp:5:7: note: constexpr Test::Test(const Test&)
test_initializer.cpp:5:7: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘const Test&’
test_initializer.cpp:5:7: note: constexpr Test::Test(Test&&)
test_initializer.cpp:5:7: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘Test&&’
I suspect this happens because the compiler can't figure out what type to make the initializer list. Is there a way to fix the example so that it works with different types (and still uses initializer lists)?
An std::initializer_list takes only one type. If you need different types, you can use variadic templates:
template<typename... Args>
Test(Args&&... args);
/* ... */
int main()
{
Test(1, 2.0);
}
Would a std::tuple<int.double> work for the OP? If the code will always have a int followed by a double, then the OP could get strict type-checking for all arguments, which the variable arguments solution does not allow. The std::tuple<>, however, would not work for any number or order of values, so may not be appropriate for all use cases.
Let the initializer_list hold the most arbitrary pointers, void*, and do your own casting from there. Here is an example.
#include <initializer_list>
#include <iostream>
using std::initializer_list;
using std::cout;
using std::endl;
class Person {
private:
string _name;
int _age;
public:
Person(initializer_list<void*> init_list) {
auto it = init_list.begin();
_name = *((string*)(*it));
it++;
_age = *((int*)(*it));
}
void print() {
cout << "name: " << _name << ". age: " << _age << endl;
}
};
int main(void) {
string name{"Vanderbutenburg};
int age{23};
Person p{&name,&age};
p.print(); // "name: Vanderbutenburg. age: 23"
return 0;
}