c++ : std::visit not compilable under gcc - c++

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

Related

How to implement std::any?

Try to implement a tiny-any:
#include <iostream>
#include <type_traits>
#include <memory>
class TinyAny
{
public:
template <typename Tp>
TinyAny(Tp&& val) : data(std::make_shared<unsigned char[]>(sizeof(val))) {
new(data.get()) Tp(std::forward<Tp>(val));
}
template <typename Tp>
TinyAny &operator =(Tp&& val)
{
data = std::make_shared<unsigned char[]>(sizeof(val));
new(data.get()) Tp(std::forward<Tp>(val));
return *this;
}
template <typename Tp>
Tp get()
{
return *reinterpret_cast<Tp *>(data.get());
}
private:
std::shared_ptr<unsigned char[]> data;
};
int main() {
// var = "abc";
// std::cout << var.get<const char *>() << std::endl;
}
if uncomment var = "abc" will get the fellowing error:
<source>: In instantiation of 'TinyAny& TinyAny::operator=(Tp&&) [with Tp = const char (&)[4]]':
<source>:40:11: required from here
<source>:17:9: error: new cannot be applied to a reference type
17 | new(data.get()) Tp(std::forward<Tp>(val));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:17:44: error: invalid conversion from 'const char*' to 'char' [-fpermissive]
17 | new(data.get()) Tp(std::forward<Tp>(val));
| ~~~~~~~~~~~~~~~~^~~~~
| |
| const char*
The type of val is const char*, but I cannot understand which one is char? Or, how can I fix this error?
Source Code: https://gcc.godbolt.org/z/zW7fPc6G3
My finally implement:
#include <type_traits>
#include <utility>
class TinyAny
{
template<typename Tp>
class AnyData {
public:
static void create(void **data, Tp && val)
{
*data = new Tp(std::forward<Tp>(val));
}
static void deleter(void *data)
{
auto ptr = static_cast<Tp *>(data);
delete ptr;
}
};
public:
template <typename Tp__, typename Tp = std::decay_t<Tp__>>
TinyAny(Tp__&& val) : deleter(AnyData<Tp>::deleter) {
AnyData<Tp>::create(&data, std::forward<Tp>(val));
}
~TinyAny() {
deleter(data);
data = nullptr;
}
template <typename Tp>
TinyAny &operator = (Tp&& val)
{
TinyAny temp{std::forward<Tp>(val)};
swap(std::move(temp));
return *this;
}
template <typename Tp>
Tp get()
{
return *static_cast<Tp*>(data);
}
private:
TinyAny &swap(TinyAny && another) noexcept
{
std::swap(data, another.data);
std::swap(deleter, another.deleter);
return *this;
}
private:
void *data;
void (* deleter)(void *data);
};

failed to recognize concept type as bool in SFINAE

consider this example:
#include <iostream>
#include <utility>
template<typename T>
concept Printable = requires(const T a) {
a.print();
};
template<typename T>
constexpr auto is_printable() {
return Printable<T>;
}
template<class T, std::enable_if_t<is_printable<T>()>* = nullptr>
constexpr void do_print(T data) {
data.print();
}
struct foo {
void print() const {
std::cout << "Hello World\n";
}
};
int main() {
foo f;
do_print(f);
}
trying to compile this on MSVC (Version 16.9.4, /std:c++latest) will produce these errors:
Error C2783 'void do_print(T)': could not deduce template argument for '__formal'
Error C2672 'do_print': no matching overloaded function found
It failed to satisfy the std::enable_if_t.
I discovered the error comes from the auto in constexpr auto is_printable() { ... }, and replacing the the auto with bool will correctly compile.
template<typename T>
constexpr bool is_printable() {
return Printable<T>;
}
I find this very bizarre, the concept Printable<T> is evaluated at compile time and should produce a constexpr bool. Why does auto fail suddenly?
This is an MSVC bug. Your code is correct. I highly recommend reporting the issue. Your code works correctly on GCC and Clang.
In the meantime, I would simply drop the SFINAE. It is not really needed when you got concepts that replaces enable ifs:
#include <iostream>
#include <utility>
template<typename T>
concept Printable = requires(const T a) {
a.print();
};
constexpr void do_print(Printable auto data) {
data.print();
}
struct foo {
void print() const {
std::cout << "Hello World\n";
}
};
int main() {
foo f;
do_print(f);
}
You can also use requires or even replace typename with your concept:
template<typename T> requires Printable<T>
constexpr void do_print(T data) {
data.print();
}
template<Printable T>
constexpr void do_print(T data) {
data.print();
}

Stringify template parameters

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!

var used in its own initializer

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)};

Restricting base template instantiation

I have a code as following, here I want to throw errors if the client code does not match any of specializations. But static assert is not helping the cause. Please suggest how to achieve it.
struct storage_manager{
storage_impl<double> double_store;
storage_impl<std::string> string_store;
template<typename T>
bool try_getting(int key, T &value)
{
static_assert(false , "Compiler should not reach here");
return false;
}
storage_manager(){}
~storage_manager(){}
storage_manager(storage_manager const &) = delete;
};
Specialization for double type.
template<>
inline bool storage_manager::try_getting<double>(int key, double &value)
{
return double_store.try_getting(key,value);
}
specializations for std::string type.
template<>
inline bool storage_manager::try_getting<std::string>(int key, std::string &value)
{
return string_store.try_getting(key,value);
}
How to throw errors at compile time when an unsupported type is requested. like...
storage_manager mgr;
int a;
std::cout<<mgr.try_getting(134,a);
You can do
template<typename T>
bool try_getting(int key, T &value) = delete;
and implement only the desired specializations. Example:
#include <iostream>
template<typename> void f() = delete;
template <> void f<int>(){std::cout << "ok\n";}
int main()
{
f<int>();
// f<double>(); // does not compile
}
Live on Coliru