The following code :
auto getConnection(const std::string &name) {
constexpr const std::size_t id{findFactoryId(_factories, name)};
const auto factory = std::get<std::integral_constant<std::size_t, id>{}>(_factories).second;
for (auto &connection : _connections[id])
if (connection.first) {
connection.first = false;
decltype(factory()) &res = std::experimental::any_cast(connection.second);
return res;
}
_connections[id].emplace_back(std::make_pair<bool, std::experimental::any>(false, factory()));
decltype(factory()) &res = std::experimental::any_cast(_connections[id].back().second);
return res;
}
compile with clang++, but with g++ gives this error:
In file included from main.cpp:2:0:
src/core/include/connectionpool.h: Dans la fonction membre « auto Core::ConnectionPool<Connectors>::getConnection(const string&) »:
src/core/include/connectionpool.h:28:79: erreur : the value of « id » is not usable in a constant expression
const auto factory = std::get<std::integral_constant<std::size_t, id>{}>(_factories).second;
^~
src/core/include/connectionpool.h:27:41: note : « id » used in its own initializer
constexpr const std::size_t id{findFactoryId(_factories, name)};
^~
src/core/include/connectionpool.h:28:81: erreur : the value of « id » is not usable in a constant expression
const auto factory = std::get<std::integral_constant<std::size_t, id>{}>(_factories).second;
^
src/core/include/connectionpool.h:27:41: note : « id » used in its own initializer
constexpr const std::size_t id{findFactoryId(_factories, name)};
^~
src/core/include/connectionpool.h:28:81: note : in template argument for type « unsigned int »
const auto factory = std::get<std::integral_constant<std::size_t, id>{}>(_factories).second;
^
I'm using those command to compile:
(clan)g++ -std=c++14 -O2 -Wall -pedantic -Wextra main.cpp
with g++ v6.3.1 and clang++ v3.9.1
The only link that look like to correspond to my issue is a bug report for gcc4.9 (which is solved) : https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59937.
A minimal working example is available here.
From what I've understood of gcc error message, I should not have any error: id isn't used to initialise itself.
Should this code yield an error or not ?
If it should raise an error, what could I do to solve the error ?
Thank you for your answers.
The complete code:
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <functional>
#include <utility>
#include <type_traits>
#include <tuple>
#include <experimental/any>
template <class F, class... Ts>
constexpr void for_each_in_tuple(const std::tuple<Ts...> &tuple, F f) {
for_each_in_tuple(tuple, f, std::make_index_sequence<sizeof...(Ts)>());
}
template <class F, class... Ts, std::size_t... Is>
constexpr void for_each_in_tuple(const std::tuple<Ts...> &tuple, F f, std::index_sequence<Is...>) {
using expander = int[];
(void) expander{0, ((void)f(Is, std::get<Is>(tuple)), 0)...};
}
template <typename... Connectors>
class ConnectionPool {
public:
auto getConnection(const std::string &name) {
constexpr const std::size_t id{findFactoryId(_factories, name)};
const auto factory = std::get<std::integral_constant<std::size_t, id>{}>(_factories).second;
return factory();
}
private:
struct foo {
constexpr foo(std::size_t &i, const std::string &name) : i(i), name(name) {}
template <class T>
constexpr void operator()(const std::size_t is, const T pair) {
i = name == pair.first ? is : i;
}
std::size_t &i;
const std::string &name;
};
template <class Tuple>
static constexpr std::size_t findFactoryId(Tuple &tup, const std::string &name) {
std::size_t i = 0;
for_each_in_tuple(tup, foo(i, name));
return i;
}
std::tuple<std::pair<std::string, std::function<Connectors()>>...> _factories;
};
int main()
{
return 0;
}
EDIT
Change link to minimal working example: a function was missing.
EDIT 2
Add minimal working example in the post
The problem is on this line:
constexpr const std::size_t id{findFactoryId(_factories, name)};
The initializer of a constexpr variable must be a constant expression. In a constant expression you can not use the this pointer. You are implicitly using the this pointer by referring to _factories, which is a data member.
N4296 [expr.const] ¶2
A conditional-expression e is a core constant expression unless the evaluation of e... would evaluate one of the following expressions:
this, except in a constexpr function or a constexpr constructor that is being evaluated as part of e;
...
Surprisingly, both compilers are happy if we simply use an explicit this:
constexpr const std::size_t id{findFactoryId(this->_factories, name)};
But I do not believe that is conformant. Here is a portable workaround:
const auto _this = this;
constexpr const std::size_t id{findFactoryId(_this->_factories, name)};
Related
I am trying to compile a class using Clang:
#include <string>
#include <type_traits>
#include <cstdlib>
struct foo
{
template <class T, class... Ts>
struct is_any: std::disjunction <std::is_same <T, Ts>... >{};
template<typename T, std::size_t N>
static constexpr bool is_pointer_to_const_char(T(&)[N])
{
return std::is_same_v<const char, T>;
}
template<typename T>
static constexpr bool is_pointer_to_const_char(T &&)
{
return std::is_same_v<const char *, T>;
}
template <class T>
static constexpr bool is_str( const T& obj )
{
return is_pointer_to_const_char( obj ) || is_any<T, std::string, std::string_view>::value;
}
static constexpr bool is_escape_v( const std::string_view& str )
{
return ! str.rfind( "\033", 0 );
}
template <class T>
inline bool is_escape( const T& str ) const
{
if constexpr ( is_str( str ) ) return is_escape_v( str );
return false;
}
};
int main()
{
foo obj;
obj.is_escape( "test" );
}
But I get the following error:
prove.cpp:36:28: error: constexpr if condition is not a constant expression
if constexpr ( is_str( str ) ) return is_escape_v( str );
^
prove.cpp:44:7: note: in instantiation of function template specialization 'foo::is_escape<char [5]>' requested here
obj.is_escape( "test" );
^
1 error generated.
It seems that it doesn't like the constexpr qualifier I used in the if condition in the is_escape() method.
Did I do something wrong in the class method's definition?
My Clang version is 10.0.0-4.
Did I do something wrong in the class method's definition?
The problem is that function parameters (like str) are not constant expressions. For example, we cannot use str as a template non-type parameter, or as a size of a built-in array.
This means the expression is_str( str ) that contains the subexpression str is itself not a constant expression, either.
Moreover, the code is rejected by all the three major compilers, if you're using their latest versions. Demo
A simpler way of doing this would be as shown below:
template <typename T>
bool is_escape(const T& str)
{
if constexpr(std::is_convertible_v<T, std::string>)
{
return ! std::string(str).rfind( "\033", 0 );
}
else
{
return false;
}
}
Working demo
Or just return is_escape_v(str):
template <class T>
inline bool is_escape( const T& str ) const
{
if constexpr (std::is_convertible_v<T, std::string_view>)
{
return is_escape_v( str );
}
else
{
return false;
}
}
Demo
Here is my code which compiles fine for clang but failed with gcc
#include <iostream>
#include <string>
#include <regex>
#include <variant>
struct Id {
void SetValue(const std::string& item)
{
value = item;
}
std::string value;
};
struct Number {
void SetValue(const std::string& item)
{
value = std::stoi(item);
}
int value;
};
using TokenBase
= std::variant<Number, Id>;
struct Token : TokenBase {
using TokenBase::TokenBase;
template <typename T>
[[nodiscard]] bool Is() const {
return std::holds_alternative<T>(*this);
}
template <typename T>
[[nodiscard]] const T& As() const {
return std::get<T>(*this);
}
template <typename T>
[[nodiscard]] const T* TryAs() const {
return std::get_if<T>(this);
}
};
struct LexerTokenExtractor {
const std::string& item_;
void operator()(Number& item) const {
item.SetValue(item_);
}
void operator()(Id& item) const {
item.SetValue(item_);
}
};
int main()
{
const std::string string_token("x");
Token id_token = Id();
std::visit(LexerTokenExtractor{string_token}, id_token);
std::cout << "ok" << std::endl;
}
Here is the log :
required from here
/usr/include/c++/7/variant:97:29: error: incomplete type ‘std::variant_size<Token>’ used in nested name specifier
inline constexpr size_t variant_size_v = variant_size<_Variant>::value;
/usr/include/c++/7/variant: In instantiation of ‘constexpr const auto std::__detail::__variant::__gen_vtable<void, LexerTokenExtractor&&, Token&>::_S_vtable’:
/usr/include/c++/7/variant:711:29: required from ‘struct std::__detail::__variant::__gen_vtable<void, LexerTokenExtractor&&, Token&>’
/usr/include/c++/7/variant:1255:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = LexerTokenExtractor; _Variants = {Token&}]’
1673947047/source.cpp:65:57: required from here
/usr/include/c++/7/variant:711:49: error: ‘_S_apply’ was not declared in this scope
static constexpr auto _S_vtable = _S_apply();
Please give me any ideas of what could be wrong here
As mentioned in the comments, this is a known bug in the current versions of GCC: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90943
A simple workaround would be to force std::visit() to operate directly on the variant instead of the subclass by using a static_cast.
std::visit(LexerTokenExtractor{string_token}, static_cast<TokenBase&>(id_token));
See on godbolt: https://gcc.godbolt.org/z/vMGfahq3z
I'm having trouble expanding an array reference. When I pass the array reference to a function template as an argument, the constant nature of the array reference seems to be lost:
#include <cstdlib>
#include <utility>
template<const char ... C>
struct hint_for_opt_cls_holder {
constexpr static const char value[] { '?', C... };
};
template<size_t N, size_t... Is>
static constexpr auto
create_hint_for_opt_cls(const char (&cname)[N],
std::index_sequence<Is...>) {
// fails: cname is not a constant expression
return hint_for_opt_cls_holder<cname[Is]...>::value;
}
template<size_t N>
static constexpr auto
create_hint_for_opt_cls(const char (&cname)[N]) {
constexpr decltype(auto) cname_ = cname; // fails already here, actually
return create_hint_for_opt_cls(cname,
std::make_index_sequence<N>());
}
constexpr static char name[] = "foobar";
constexpr static auto& get_class_name() {
return name;
}
int main() {
// works! knows the return is constant
constexpr decltype(auto) cname = get_class_name();
auto x = create_hint_for_opt_cls(cname);
}
Arguments are not constexpr, you have to turn them in type:
gcc/clang have an extension to allow to build UDL from literal string:
// That template uses the extension
template<typename Char, Char... Cs>
constexpr auto operator"" _cs() -> std::integer_sequence<Char, Cs...> {
return {};
}
See my answer from String-interning at compiletime for profiling to have MAKE_STRING macro if you cannot used the extension (Really more verbose, and hard coded limit for accepted string length).
Then
template<char ... Cs>
static constexpr auto
create_hint_for_opt_cls(std::integer_sequence<char, Cs...>) {
return hint_for_opt_cls_holder<Cs...>::value;
}
constexpr static auto name = "foobar"_cs;
I am trying to extend the code from the answer to this question Unpack parameter pack into string view.
Instead of stringifying char-only types, I would like to use a constexpr_string type, custom written:
#include <array>
#include <iostream>
class constexpr_string {
public:
template <std::size_t N>
constexpr constexpr_string(const char (&s)[N]) : string_{s}, size_{N - 1} {}
constexpr constexpr_string() = default;
constexpr char const operator[](std::size_t n) const { return string_[n]; }
constexpr std::size_t GetSize() { return size_; }
private:
char const *string_{nullptr};
std::size_t size_{0};
};
template <constexpr_string... strings> constexpr auto stringify() {
std::array<constexpr_string, sizeof...(strings)> array = {strings...};
return array;
}
int main() {
static constexpr auto s = stringify<"abc", "def", "fgh">();
for (auto &i : s) {
std::cout << i << std::endl;
}
return 0;
}
When I compile though, I get :
main.cpp:18:31: error: ‘class constexpr_string’ is not a valid type for a template non-type parameter
template constexpr auto stringify()
Is such a thing even possible? I'm compiling with g++ (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0. Many thanks in advance!
I have written a constexpr function which calculate the sum of the sizes of the elements of a tuple.
When called directly, the function call compiles with both tuple of values, and tuple of references.
When called through a templated function, it still compiles with tuple of values, but fail with tuple of references.
I can work around my problem using tuple of pointers instead of tuple of reference, but the API of the thing I write (a templated set of functions to ease writing SPI and I²C driver for microcontrollers) will be less clean.
Thanks for any help.
Ps: I am using gcc8.2 using c++17 standard.
Alexandre
#include <tuple>
template <typename> struct is_tuple_t: std::false_type {};
template <typename ...T> struct is_tuple_t<std::tuple<T...>> : std::true_type {};
template<typename Type>
constexpr bool is_tuple(const Type&) {
if constexpr (is_tuple_t<Type>::value)
return true;
else
return false;
}
template<class F, class...Ts, std::size_t...Is>
constexpr void for_each_in_tuple(const std::tuple<Ts...> & tupl, F func,
std::index_sequence<Is...>){
using expander = int[];
(void)expander { 0, ((void)func(std::get<Is>(tupl)), 0)... };
}
template<class F, class...Ts>
constexpr void for_each_in_tuple(const std::tuple<Ts...> & tupl, F func){
for_each_in_tuple(tupl, func, std::make_index_sequence<sizeof...(Ts)>());
}
template <typename T>
constexpr size_t size_of_tuple(const T &tup) {
static_assert(is_tuple(tup) == true, "error size_of_tuple argument must be a tuple");
size_t s=0;
for_each_in_tuple(tup, [&s](auto &&x) {
s += sizeof(x);
});
return s;
}
template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
constexpr size_t st = size_of_tuple(tup);
return st;
}
int main()
{
uint16_t var;
constexpr size_t s1 = size_of_tuple(std::make_tuple(1)) ; // OK
constexpr size_t s2 = size_of_tuple(std::forward_as_tuple(var)) ; // OK
constexpr size_t f1 = foo(std::make_tuple(1)) ; // OK
constexpr size_t f2 = foo(std::forward_as_tuple(var)) ; // FAIL
}
template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
constexpr size_t st = size_of_tuple(tup);
return st;
}
In this function, tup is not a constant expression, so it can't be used in the initializer of a constexpr variable.
[expr.const]¶2
An expression e is a core constant expression unless the evaluation of e [...] would evaluate one of the following expressions:
[...]
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
it is initialized with a constant expression or
its lifetime began within the evaluation of e
Either one of these should work instead:
template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
size_t st = size_of_tuple(tup);
return st;
}
template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
return size_of_tuple(tup);
}
Note that Clang still rejects your code, for other violations of the same rule.
Ultimately, your size_of_tuple and is_tuple are both flawed, as they can't be used in constant expressions if their argument is a reference. If you want to use this function-like syntax, you need something like type_c from Boost.Hana:
template <typename T>
class type {};
template <typename T>
constexpr type<T> type_c{};
template <typename T>
constexpr bool is_tuple(type<T>) {
return is_tuple_t<T>::value;
}
template <typename T>
constexpr size_t size_of_tuple(type<T> tup) {
static_assert(is_tuple(tup), "size_of_tuple argument must be a tuple");
//...
}
template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
size_t st = size_of_tuple(type_c<std::remove_cvref_t<decltype(tup)>>);
return st;
}
uint16_t var = 42;
constexpr size_t f2 = foo(std::forward_as_tuple(var));
The problems of your original approach are properly explained in Oktalist's answer.
Note that size_of_tuple can be implemented in one line using C++17's fold expressions:
template<class... Ts>
constexpr std::size_t size_of_tuple(const std::tuple<Ts...>&) {
return (0 + ... + sizeof(Ts));
}
However, that function is hard to use because of the argument of type const std::tuple<Ts...>&. Thus it may be desirable to introduce an empty tag type that can be "passed" to these kind of metafunctions. This idea is explained in Boost.Hana's chapter on type computations.
The following is a complete example.
static_assert(__cplusplus >= 201703L, "C++17 required");
#include <cstddef>
#include <cstdint>
#include <tuple>
// the empty tag type
template<class T>
struct Type {};
////////////////////////////////////////////////////////////////////////////////
template<class... Ts>
constexpr std::size_t size_of_tuple(Type< std::tuple<Ts...> >) {
return (0 + ... + sizeof(Ts));
}
static_assert(0 == size_of_tuple(Type< std::tuple<> >{}));
static_assert(12 == size_of_tuple(Type< std::tuple<int32_t, int64_t> >{}));
static_assert(12 == size_of_tuple(Type< std::tuple<int32_t&, int64_t&> >{}));
static_assert(2 == size_of_tuple(Type< std::tuple<char&, char> >{}));
static_assert(6 == size_of_tuple(Type< std::tuple<int32_t&, char&, char> >{}));
// fails to compile (for good reasons):
//static_assert(8 == size_of_tuple(Type< std::tuple<int32_t, uint64_t> >{}));
Furthermore, you can consider to use std::integral_constant as return type. That may become handy when you want to pass the size into another function and use it as a constexpr value. Note that std::integral_constants are implicitly convertible to their ::type. The implicit conversion is one reason why Boost.Hana introduces its own "integral-constant type". Some interesting examples can be found in Boost.Hana's chapter on Compile-time numbers. Anyway, here is the simple example with the implicitly convertible std::integral_constant:
#include <cstddef>
#include <tuple>
#include <type_traits>
template<class... Ts>
constexpr auto better_size_of_tuple(Type< std::tuple<Ts...> >) {
constexpr std::size_t ret = (0 + ... + sizeof(Ts));
return std::integral_constant<std::size_t, ret>{};
}
so here is a rewrite of the function which compile, at least with gcc 8.2 :
template
<std::size_t position, class T>
struct SizeOfTupImpl{
static constexpr size_t
size() {
if constexpr (position != 0) {
return sizeof(std::tuple_element_t<position, T>) +
SizeOfTupImpl<position - 1, T>::size();
} else {
return sizeof(std::tuple_element_t<0, T>);
}
}
};
template<class T>
constexpr size_t
size_of_tuple(const T& tup) {
static_assert(is_tuple(tup) == true, "error size_of_tuple argument must be a tuple");
constexpr std::size_t dimension = std::tuple_size<T>::value;
return SizeOfTupImpl<dimension - 1, T>::size();
}
Since i am an absolute template metaprogramming beginner, feel free to point me toward a more elegant solution !
Alexandre