I'm working on a C++ project that uses two different libraries: spdlog for logging, and mutils-serialization for serializing objects to bytes (for sending over the network). Both libraries use namespaces properly, but when I attempt to write a program that uses both of them at the same time, my compiler (g++ 6.2) gives me nonsensical errors that seem to indicate it is attempting to instantiate a function template from the spdlog library by using the definition of a function template from the mutils library.
Here's my simple test program:
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h>
#include "TestSerializableObject.h"
int main(int argc, char** argv) {
auto global_logger = spdlog::rotating_logger_mt("global_logger", "log", 1024 * 1024 * 500, 3);
global_logger->set_pattern("[%H:%M:%S.%e] [%l] %v");
global_logger->set_level(spdlog::level::trace);
std::shared_ptr<spdlog::logger> logger(spdlog::get("global_logger"));
auto message = std::make_shared<messaging::TestSerializableObject>(
1, 2, "A message!");
logger->trace("Received a message: {}", *message);
}
TestSerializableObject is a simple class that implements mutils::ByteRepresentable (the interface that enables serialization and pulls in the mutils-serialization library), and provides an operator<< (which is required for spdlog to be able to log it). I can post the code for it if necessary.
When I compile this with g++ -std=c++14 -I"./src" -I"./libraries" -I"./libraries/mutils/" -L"./libraries/" -O0 -g3 -Wall "src/LibraryCollisionTest.cpp", I get this long, ugly error (don't worry, I'll help you parse it):
In file included from ./libraries/mutils/mutils.hpp:3:0,
from ./libraries/mutils-serialization/SerializationSupport.hpp:2,
from src/TestSerializableObject.h:10,
from src/LibraryCollisionTest.cpp:10:
./libraries/mutils/args-finder.hpp: In instantiation of ‘struct mutils::function_traits<messaging::TestSerializableObject>’:
./libraries/mutils/args-finder.hpp:75:41: required from ‘auto mutils::convert(F) [with F = messaging::TestSerializableObject; ignore = void]’
./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12: required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5: required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9: required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5: required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53: required from here
./libraries/mutils/args-finder.hpp:12:37: error: ‘operator()’ is not a member of ‘messaging::TestSerializableObject’
: public function_traits<decltype(&T::operator())>
^~
./libraries/mutils/args-finder.hpp: In instantiation of ‘auto mutils::convert(F) [with F = messaging::TestSerializableObject; ignore = void]’:
./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12: required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5: required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9: required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5: required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53: required from here
./libraries/mutils/args-finder.hpp:75:41: error: ‘as_function’ is not a member of ‘mutils::function_traits<messaging::TestSerializableObject>’
return function_traits<F>::as_function(f);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
In file included from ./libraries/spdlog/fmt/fmt.h:21:0,
from ./libraries/spdlog/common.h:41,
from ./libraries/spdlog/spdlog.h:12,
from src/LibraryCollisionTest.cpp:8:
./libraries/spdlog/fmt/bundled/format.h: In instantiation of ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’:
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12: required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5: required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9: required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5: required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53: required from here
./libraries/spdlog/fmt/bundled/format.h:1276:38: warning: invalid application of ‘sizeof’ to a void type [-Wpointer-arith]
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
The key line is here:
./libraries/mutils/args-finder.hpp: In instantiation of ‘auto mutils::convert(F)
[with F = messaging::TestSerializableObject; ignore = void]’:
./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct
fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of
‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const
T&, typename fmt::internal::EnableIf<fmt::internal::Not<
fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T =
messaging::TestSerializableObject]’
Somehow, g++ has jumped from expanding a templated function inside the spdlog library, in namespace fmt::internal, to a function template in the mutils library, in namespace mutils, which is clearly not what the spdlog library intended to do! If I look at line 1276 of format.h, it's the one that calls the "convert" function inside this template struct:
template<typename T>
struct ConvertToInt
{
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
};
A few lines above, sure enough, is the function "convert":
template <typename T>
T &get();
Yes &convert(fmt::ULongLong);
No &convert(...);
These are all inside the namespace fmt::internal, and my IDE agrees that if I want the definition of the function "convert" on line 1276, I should jump to the function "convert" on line 1248. So why does g++ ignore this definition, and instead try to use the definition for mutils::convert(), which isn't even in the right namespace?
Note that clang also fails to compile this program, and makes the same mistake, so I don't think that this is a bug in g++.
This is definitively a bug in spdlog fmtlib, used internally by spdlog.
The problem is described summarily in this FAQ:
What is “Argument-Dependent Lookup” (aka ADL, or “Koenig Lookup”)?
Because messaging::TestSerializableObject inherits from a type in namespace mutils, when convert is called unqualified from inside namespace fmt::internal with a TestSerializableObject, both fmt::internal::convert and mutils::convert are considered in the overload set. Variadic functions always rank last during overload resolution, so the template argument F in the latter is a better match than the ... in the former and mutils::convert is chosen.
This is in no way specific to your code or to mutils – any type with a unary function or function template named convert in the same namespace or a parent namespace is susceptible to this problem.
The fix is to qualify the convert call and change the definition of fmt::internal::ConvertToInt<T>::enable_conversion from
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
to
enum { enable_conversion = sizeof(internal::convert(get<T>())) == sizeof(Yes) };
In my own code I make a habit of always qualifying all calls to functions inside any internal/detail namespace, even from code inside that same namespace, unless ADL usage is explicitly intended. (N.b. calls don't need to be fully qualified, merely qualified.) I learned this lesson from watching Boost have to deal with this problem the hard way as C++11 was emerging.
Related
While working on a project I encounter a situation std::apply does not forward rvalue references from std::tuple created by std::forward_as_tuple *IF* resulting std::tuple is stored in a variable! However if std::forward_as_tuple result is not stored in a variable, but is just passed as a second argument to std::apply then it works and rvalue reference gets perfectly forwarded.
I tried many options including using different types for std::tuple, like
decltype(auto) t = forward_as_tuple(1, std::move(r))
auto t = forward_as_tuple(1, std::move(r))
auto&& t = forward_as_tuple(1, std::move(r))
auto& t = forward_as_tuple(1, std::move(r))
Nothing helped to store a tuple in a variable and then pass it to std::apply. It appears like lvalue reference being forwarded at the end to std::__invoke by std::apply...
There is a godbolt link to my code: https://godbolt.org/z/24LYP5
Code snippet
#include <functional>
#include <iostream>
auto product(int l, int&& r) { return l * r; }
static void test_not_works()
{
int r = 2;
decltype(auto) t = std::forward_as_tuple(1, std::move(r));
std::apply(product, t);
}
static void test_works()
{
int r = 2;
std::apply(product, std::forward_as_tuple(1, std::move(r)));
}
Error message
In file included from /opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/functional:54:0,
from <source>:1:
/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple: In instantiation of 'constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = int (&)(int, int&&); _Tuple = std::tuple<int&&, int&&>&; long unsigned int ..._Idx = {0, 1}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1>]':
/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1671:31: required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = int (&)(int, int&&); _Tuple = std::tuple<int&&, int&&>&]'
<source>:10:26: required from here
/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1662:27: error: no matching function for call to '__invoke(int (&)(int, int&&), int&, int&)'
return std::__invoke(std::forward<_Fn>(__f),
~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
std::get<_Idx>(std::forward<_Tuple>(__t))...);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:41:0,
from /opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/functional:54,
from <source>:1:
/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:89:5: note: candidate: template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...)
__invoke(_Callable&& __fn, _Args&&... __args)
^~~~~~~~
/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:89:5: note: template argument deduction/substitution failed:
/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h: In substitution of 'template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = int (&)(int, int&&); _Args = {int&, int&}]':
/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1662:27: required from 'constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = int (&)(int, int&&); _Tuple = std::tuple<int&&, int&&>&; long unsigned int ..._Idx = {0, 1}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1>]'
/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/tuple:1671:31: required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = int (&)(int, int&&); _Tuple = std::tuple<int&&, int&&>&]'
<source>:10:26: required from here
/opt/compiler-explorer/gcc-7.3.0/include/c++/7.3.0/bits/invoke.h:89:5: error: no type named 'type' in 'struct std::__invoke_result<int (&)(int, int&&), int&, int&>'
Compiler returned: 1
When rvalue references are passed to std::forward_as_tuple, it constructs a std::tuple of rvalue references. So first of all, declaring t as auto t = std::forward_as_tuple(...) is fine, the "rvalueness" of the object is encoded inside the type generated by std::forward_as_tuple.
But then note that there is something special about the first argument to std::apply: product takes an int parameter by value, and a second one as int&&, i.e., an rvalue-reference. Calling such a function obviously requires the second argument to be an rvalue reference, but this does only work if you make sure it is one. Hence:
auto t = std::forward_as_tuple(1, std::move(r));
std::apply(product, std::move(t));
// ^^^^^^^ cast to rvalue here
Another possible fix is to change product to accept to plain ints by value (in case of an int, there is no performance hit anyway). Then, you can pass t as an lvalue, too.
std::apply perfect-forwards the tuple to std::get to access elements of the tuple. std::get returns an lvalue reference if its tuple argument is an lvalue. Since your t variable is an lvalue, std::apply calls product with an int& instead of an int&&.
I can't speak for the motivation behind having it work this way despite the tuple explicitly holding an rvalue reference. However, a simple way to get the same behaviour as a temporary here is to use std::move to produce an xvalue, which will handle stored references in the way you want:
std::apply(product, std::move(t));
Note, however, that unlike using just t, any non-reference types in the tuple should now be treated as moved from. I'm unsure if there's a simple way to both use the tuple's reference types and treat non-reference types as lvalues for an lvalue tuple. Granted this won't be an issue when creating the tuple using forward_as_tuple since it will always contain references.
This is a continuation of When is a class member visible?
After making the class compile with GCC by moving the declaration of pk_ to the beginning, I tried to use it:
#include <string>
#include <map>
#include <type_traits>
using from_type = std::map<std::string, std::string>;
template<typename PK, size_t N>
struct pf {
public:
PK pk_;
pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fill(pk_, std::string{})))
: map_(a) // GCC 4.8 requires ()s for references
, pk_{ [&]{ fill(pk_, pkn); return pk_; }() }
{
}
template<typename prop_t>
typename std::enable_if<
std::is_integral<typename std::decay<prop_t>::type>::value,
pf<PK, N>>::type const&
fill(prop_t& , std::string const& , prop_t = 0) const noexcept(false);
pf<PK, N> const&
fill(std::string& , std::string const&) const noexcept;
protected:
from_type const& map_;
uint32_t aieee;
};
std::string k;
from_type m;
int i;
std::string s;
static_assert(!noexcept(pf<int , 42>{m, k}), "int could throw");
static_assert( noexcept(pf<std::string, 17>{m, k}), "string shouldn't throw");
clang 4.0, 6.0 and trunk again compiled the program.
GCC was still not happy:
$ g++-99 -Wall -pedantic -Wextra -Wformat=2 -std=c++14 pf.cpp
pf.cpp: In instantiation of ‘pf<PK, N>::pf(const from_type&, const string&) [with PK = int; long unsigned int N = 42; from_type = std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >; std::string = std::__cxx11::basic_string<char>]’:
pf.cpp:38:49: required from here
pf.cpp:12:74: error: no matching function for call to ‘fill(int&, std::__cxx11::basic_string<char>)’
12 | pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fill(pk_, std::string{})))
| ~~~~^~~~~~~~~~~~~~~~~~~~
In file included from /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/char_traits.h:39,
from /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/string:40,
from pf.cpp:1:
/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h:742:5: note: candidate: ‘template<class _ForwardIterator, class _Tp> void std::fill(_ForwardIterator, _ForwardIterator, const _Tp&)’
742 | fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value)
| ^~~~
/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h:742:5: note: template argument deduction/substitution failed:
pf.cpp:12:74: note: deduced conflicting types for parameter ‘_ForwardIterator’ (‘int’ and ‘std::__cxx11::basic_string<char>’)
12 | pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fill(pk_, std::string{})))
| ~~~~^~~~~~~~~~~~~~~~~~~~
pf.cpp: In instantiation of ‘pf<PK, N>::pf(const from_type&, const string&) [with PK = std::__cxx11::basic_string<char>; long unsigned int N = 17; from_type = std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >; std::string = std::__cxx11::basic_string<char>]’:
pf.cpp:39:49: required from here
pf.cpp:12:74: error: no matching function for call to ‘fill(std::__cxx11::basic_string<char>&, std::__cxx11::basic_string<char>)’
In file included from /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/char_traits.h:39,
from /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/string:40,
from pf.cpp:1:
/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h:742:5: note: candidate: ‘template<class _ForwardIterator, class _Tp> void std::fill(_ForwardIterator, _ForwardIterator, const _Tp&)’
742 | fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value)
| ^~~~
/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h:742:5: note: template argument deduction/substitution failed:
pf.cpp:12:74: note: candidate expects 3 arguments, 2 provided
12 | pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fill(pk_, std::string{})))
| ~~~~^~~~~~~~~~~~~~~~~~~~
pf.cpp:39:16: error: static assertion failed: string shouldn't throw
39 | static_assert( noexcept(pf<std::string, 17>{m, k}), "string shouldn't throw");
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This was confusing (who said anything about iterators?) until I saw the answer to my first question. The compiler couldn't see the fill members, so it tried the only fill method available to it: the one from <algorithm>, which was accidentally included via
. /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/string
.. /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/char_traits.h
... /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h
So I renamed the fill members to fillz (including the one in the noexcept operator), which resulted in:
$ g++-99 -Wall -pedantic -Wextra -Wformat=2 -std=c++14 pf.cpp
pf.cpp: In instantiation of ‘pf<PK, N>::pf(const from_type&, const string&) [with PK = int; long unsigned int N = 42; from_type = std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >; std::string = std::__cxx11::basic_string<char>]’:
pf.cpp:38:49: required from here
pf.cpp:12:75: error: ‘fillz’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
12 | pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fillz(pk_, std::string{})))
| ~~~~~^~~~~~~~~~~~~~~~~~~~
pf.cpp:12:75: note: declarations in dependent base ‘pf<int, 42>’ are not found by unqualified lookup
pf.cpp:12:75: note: use ‘pf::fillz’ instead
pf.cpp:12:75: error: cannot call member function ‘const typename std::enable_if<std::is_integral<typename std::decay<prop_t>::type>::value, pf<PK, N> >::type& pf<PK, N>::fillz(prop_t&, const string&, prop_t) const [with prop_t = int; PK = int; long unsigned int N = 42; typename std::enable_if<std::is_integral<typename std::decay<prop_t>::type>::value, pf<PK, N> >::type = pf<int, 42>; std::string = std::__cxx11::basic_string<char>]’ without object
pf.cpp: In instantiation of ‘pf<PK, N>::pf(const from_type&, const string&) [with PK = std::__cxx11::basic_string<char>; long unsigned int N = 17; from_type = std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >; std::string = std::__cxx11::basic_string<char>]’:
pf.cpp:39:49: required from here
pf.cpp:12:75: error: ‘fillz’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
pf.cpp:12:75: note: declarations in dependent base ‘pf<std::__cxx11::basic_string<char>, 17>’ are not found by unqualified lookup
pf.cpp:12:75: note: use ‘pf::fillz’ instead
pf.cpp:12:75: error: cannot call member function ‘const pf<PK, N>& pf<PK, N>::fillz(std::string&, const string&) const [with PK = std::__cxx11::basic_string<char>; long unsigned int N = 17; std::string = std::__cxx11::basic_string<char>]’ without object
pf.cpp:39:16: error: static assertion failed: string shouldn't throw
39 | static_assert( noexcept(pf<std::string, 17>{m, k}), "string shouldn't throw");
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Obviously GCC can still not see the member (now named fillz), but why does it complain about dependent bases? Struct pf has nothing to do with inheritance. Could it be because this kind of visibility problem comes up most often with dependent bases?
In the end, the correct usage turned out to be:
noexcept(noexcept(std::declval<pf&>().fill(std::declval<PK&>(), std::string{})))
At first glance, this seems like it could be more than one bug in GCC.
(All standard quotes are from C++14, as you are compiling with -std=c++14. All bold is added.)
[basic.scope.class]¶1
The potential scope of a name declared in a class consists [...] also of all function bodies, default arguments, exception-specifications, and brace-or-equal-initializers of non-static data members in that class (including such things in nested classes).
So in the noexcept clause of a member function declaration, all members of the class are in scope regardless of the order in which they are declared.
But this isn't enough to convince ourselves that your code is well-formed. We are in a class template, so next we need to consider two-phase lookup:
[temp.dep]¶1
In an expression of the form:
postfix-expression ( expression-listopt )
where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if
(1.1) any of the expressions in the expression-list is a pack expansion,
(1.2) any of the expressions in the expression-list is a type-dependent expression, or
(1.3) if the unqualified-id is a template-id in which any of the template arguments depends on a template parameter.
Such names are unbound and are looked up at the point of the template instantiation in both the context of the template definition and the context of the point of instantiation.
Therefore fill is a dependent name in fill(pk_, std::string{}) because pk_ is type-dependent, so fill will be looked up when the template is instantiated, both in the context of the template itself and the point where it was instantiated. Due to ADL and the fact that one of the arguments is std::string, the std namespace is included in the lookup, leading to the messages about std::fill. But lookup should also find the member function fill, as previously discussed.
Now we have convinced ourselves that the code is well-formed. We could simply file a bug and move on. But let's keep digging to see how well we understand what's happening.
[temp.dep]¶3
In the definition of a class or class template, if a base class depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
The error messages seem to indicate that GCC is (wrongly) considering the enclosing class of fill to be a dependent base of fill, and hence applying the wording of [temp.dep]¶3.
The usual way to refer to names in dependent bases is to refer to them through a qualified-id (e.g. pf::fill) or a class member access expression (e.g. this->fill). So what happens when we try either of these approaches as a workaround?
Writing this->fill seems to work in your example code, but as noted by #n.m. in the comments, this is brittle and doesn't work in a more minimal example, yielding the error "invalid use of 'this' at top level".
Writing pf::fill yields the error "cannot call member function without object". This is probably for the same reason that this->fill failed: if this is invalid then it must also be invalid to transform an id-expression into a implicit member access expression.
[expr.prim.general]/3
If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq and the end of the function-definition, member-declarator, or declarator.
So this is valid to appear in an exception-specification and GCC is wrong to reject it, and this looks on the face of it to be a bug separate from the one we already diagnosed. This last one is already known as 52869, but the testcase submitted with the bugfix failed to exercise the case in which the noexcept appears before the declaration of the member function it refers to, as in your case. So it's unclear whether your first set of errors are really part of the same bug.
I have followed the Boost tutorial, "Passing Slots (Intermediate)", at reference [ 1 ] to the letter yet I am getting a weird error with my code that I cannot decipher or find any help with regard to. My code is below:
[ 1 ] - http://www.boost.org/doc/libs/1_55_0/doc/html/signals2/tutorial.html#idp204830936
namespace GekkoFyre {
class TuiHangouts {
private:
typedef boost::signals2::signal<void()> onRosterUpdate;
typedef onRosterUpdate::slot_type onRosterUpdateSlotType;
void logMsgs(std::string message, const xmppMsgType &msgType);
void logMsgsDrawHistory();
// Slots
boost::signals2::connection doOnRosterUpdate(const onRosterUpdateSlotType &slot);
onRosterUpdate rosterUpdate;
};
}
boost::signals2::connection GekkoFyre::TuiHangouts::doOnRosterUpdate(
const GekkoFyre::TuiHangouts::onRosterUpdateSlotType &slot)
{
return rosterUpdate.connect(slot);
}
The problem is with this specifically:
void GekkoFyre::TuiHangouts::logMsgs(std::string message, const xmppMsgType &msgType)
{
doOnRosterUpdate(&GekkoFyre::TuiHangouts::logMsgsDrawHistory);
}
I get the error:
In file included from /usr/include/boost/function/detail/maybe_include.hpp:13:0,
from /usr/include/boost/function/detail/function_iterate.hpp:14,
from /usr/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:47,
from /usr/include/boost/function.hpp:64,
from /usr/include/boost/signals2/signal.hpp:18,
from /usr/include/boost/signals2.hpp:19,
from /home/phobos/Programming/gecho/src/tui/chat.hpp:47,
from /home/phobos/Programming/gecho/src/tui/chat.cpp:35:
/usr/include/boost/function/function_template.hpp: In instantiation of 'void boost::function0<R>::assign_to(Functor) [with Functor = void (GekkoFyre::TuiHangouts::*)(); R = void]':
/usr/include/boost/function/function_template.hpp:722:7: required from 'boost::function0<R>::function0(Functor, typename boost::enable_if_c<(boost::type_traits::ice_not<(boost::is_integral<Functor>::value)>::value), int>::type) [with Functor = void (GekkoFyre::TuiHangouts::*)(); R = void; typename boost::enable_if_c<(boost::type_traits::ice_not<(boost::is_integral<Functor>::value)>::value), int>::type = int]'
/usr/include/boost/function/function_template.hpp:1071:16: required from 'boost::function<R()>::function(Functor, typename boost::enable_if_c<(boost::type_traits::ice_not<(boost::is_integral<Functor>::value)>::value), int>::type) [with Functor = void (GekkoFyre::TuiHangouts::*)(); R = void; typename boost::enable_if_c<(boost::type_traits::ice_not<(boost::is_integral<Functor>::value)>::value), int>::type = int]'
/usr/include/boost/function/function_template.hpp:1126:5: required from 'typename boost::enable_if_c<(boost::type_traits::ice_not<(boost::is_integral<Functor>::value)>::value), boost::function<R()>&>::type boost::function<R()>::operator=(Functor) [with Functor = void (GekkoFyre::TuiHangouts::*)(); R = void; typename boost::enable_if_c<(boost::type_traits::ice_not<(boost::is_integral<Functor>::value)>::value), boost::function<R()>&>::type = boost::function<void()>&]'
/usr/include/boost/signals2/detail/slot_template.hpp:160:24: required from 'void boost::signals2::slot<R(Args ...), SlotFunction>::init_slot_function(const F&) [with F = void (GekkoFyre::TuiHangouts::*)(); SlotFunction = boost::function<void()>; R = void; Args = {}]'
/usr/include/boost/signals2/detail/slot_template.hpp:85:27: required from 'boost::signals2::slot<R(Args ...), SlotFunction>::slot(const F&) [with F = void (GekkoFyre::TuiHangouts::*)(); SlotFunction = boost::function<void()>; R = void; Args = {}]'
/home/phobos/Programming/gecho/src/tui/chat.cpp:802:74: required from here
/usr/include/boost/function/function_template.hpp:924:9: error: no class template named 'apply' in 'struct boost::detail::function::get_invoker0<boost::detail::function::member_ptr_tag>'
handler_type;
If anyone could assist with this then it would be immensely appreciated. As I said previously, I did some research and couldn't really find much of anything. It seems kind of unique to me, perhaps, and I did follow the tutorial to the letter. I know this isn't always the right thing to do but from what I can gather through research, this code should work.
&GekkoFyre::TuiHangouts::logMsgsDrawHistory is a member function pointer, which has the type void (GekkoFyre::TuiHangouts::*)(). This isn't like any other function, and so it cannot be called like any other function. Signals2 will attempt to call this with the syntax func(), but there is no this pointer here. To provide it with a this pointer, you would use the syntax (p->*func)(). The p here becomes the this pointer. boost::bind (also in the C++ standard since 2011 called std::bind) will wrap this up as a function object that can be called as func() by calling (p->*func)().
Let me start with explaining what I try to accomplish. I need to create a type-erased functor (using templates and virtual functions) that would be able to "emplace" a new object in the storage of message queue for the RTOS I'm developing. Such "trickery" is required, because I want most of message queue's code to be non-templated, with only the parts that really need the type info implemented as such type-erased functors. This is a project for embedded microcontrollers (*), so please assume that I just cannot make whole message queue with a template, because ROM space is not unlimited in such environment.
I already have functors that can "copy-construct" and "move-construct" the object into the queue's storage (for "push" operations) and I also have a functor that can "swap" the object out of the queue's storage (for "pop" operations). To have a complete set I need a functor that will be able to "emplace" the object into the queue's storage.
So here is the minimum example that exhibits the problem I'm facing with creating it. Do note that this is a simplified scenario, which doesn't show much of the boiler plate (there are no classes, no inheritance and so on), but the error is exactly the same, as the root cause is probably the same too. Also please note, that the use of std::bind() (or a similar mechanism that would NOT use dynamic allocation) is essential to my use case.
#include <functional>
template<typename T, typename... Args>
void emplacer(Args&&... args)
{
T value {std::forward<Args>(args)...};
}
template<typename T, typename... Args>
void emplace(Args&&... args)
{
auto boundFunction = std::bind(emplacer<T, Args...>,
std::forward<Args>(args)...);
boundFunction();
}
int main()
{
int i = 42;
emplace<int>(i); // <---- works fine
emplace<int>(42); // <---- doesn't work...
}
When compiled on PC with g++ -std=c++11 test.cpp the first instantiation (the one using a variable) compiles with no problems, but the second one (which uses a constant 42 directly) throws this error messages:
test.cpp: In instantiation of ‘void emplace(Args&& ...) [with T = int; Args = {int}]’:
test.cpp:21:17: required from here
test.cpp:13:16: error: no match for call to ‘(std::_Bind<void (*(int))(int&&)>) ()’
boundFunction();
^
In file included from test.cpp:1:0:
/usr/include/c++/4.9.2/functional:1248:11: note: candidates are:
class _Bind<_Functor(_Bound_args...)>
^
/usr/include/c++/4.9.2/functional:1319:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args)
^
/usr/include/c++/4.9.2/functional:1319:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1315:37: error: cannot bind ‘int’ lvalue to ‘int&&’
= decltype( std::declval<_Functor>()(
^
/usr/include/c++/4.9.2/functional:1333:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) const
^
/usr/include/c++/4.9.2/functional:1333:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1329:53: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘const int’
typename add_const<_Functor>::type>::type>()(
^
/usr/include/c++/4.9.2/functional:1347:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) volatile
^
/usr/include/c++/4.9.2/functional:1347:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1343:70: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘volatile int’
typename add_volatile<_Functor>::type>::type>()(
^
/usr/include/c++/4.9.2/functional:1361:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) const volatile
^
/usr/include/c++/4.9.2/functional:1361:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1357:64: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘const volatile int’
typename add_cv<_Functor>::type>::type>()(
I tried looking for inspiration in other places, but Intel's TBB library which has a similar code (concurent_queue) with similar functionality (there's an emplace function) is actually no emplace at all - it constructs the object instantly and just "moves" it into the queue...
Any idea what's wrong with the code above? I suppose it's something really small, but I just cannot solve that myself...
(*) - https://github.com/DISTORTEC/distortos
You've already had an explanation of how that is just how std::bind works (it turns everything into an lvalue), and to use a lambda instead. However, that is not exactly trivial. Lambdas can capture by value, or by reference. You sort of need a mix of both: rvalue references should be assumed to possibly reference temporaries, so should be captured by value, with move semantics. (Note: that does mean that the original object gets moved from before the lambda gets invoked.) Lvalue references should be captured by reference, for probably obvious reasons.
One way to make this work is to manually put the captured arguments in a tuple of lvalue reference types and non-reference types, and unpack when you want to invoke the function:
template <typename T>
struct remove_rvalue_reference {
typedef T type;
};
template <typename T>
struct remove_rvalue_reference<T &&> {
typedef T type;
};
template <typename T>
using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type;
template <typename F, typename...T, std::size_t...I>
decltype(auto) invoke_helper(F&&f, std::tuple<T...>&&t,
std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::move(t))...);
}
template <typename F, typename...T>
decltype(auto) invoke(F&&f, std::tuple<T...>&&t) {
return invoke_helper<F, T...>(std::forward<F>(f), std::move(t),
std::make_index_sequence<sizeof...(T)>());
}
template<typename T, typename... Args>
void emplacer(Args&&... args) {
T{std::forward<Args>(args)...};
}
template<typename T, typename...Args>
void emplace(Args&&...args)
{
auto boundFunction =
[args=std::tuple<remove_rvalue_reference_t<Args>...>{
std::forward<Args>(args)...}]() mutable {
invoke(emplacer<T, Args...>, std::move(args));
};
boundFunction();
}
When calling emplace with args T1 &, T2 &&, the args will be captured in a tuple<T1 &, T2>. The tuple gets unpacked (thanks to #Johannes Schaub - litb for the basic idea) when finally invoking the function.
The lambda needs to be mutable, to allow that captured tuple to be moved from when invoking the function.
This uses several C++14 features. Most of these can be avoided, but I don't see how to do this without the ability to specify an initialiser in the capture list: C++11 lambdas can only capture by reference (which would be reference to the local variable), or by value (which would make a copy). In C++11, I think that means the only way to do it is not use a lambda, but effectively re-create most of std::bind.
To expand on #T.C.'s comment, you can make the code work by changing the type of the created emplacer.
auto boundFunction = std::bind(emplacer<T, Args&...>,
std::forward<Args>(args)...);
Notice the & right after Args. The reason is you're passing an rvalue to the emplace function which in turn creates emplacer(int&&). std::bind however always passes an lvalue (because it comes from its internals). With the change in place, the signature changes to emplacer(int&) (after reference collapsing) which can bind to an lvalue.
I have a question about the Functor implementation of the library Loki.
I am doing some changes in order to make it work with variadic templates instead of having lines and lines of template specialization. The problem is that I am trying to use typedef for variadic template and I do not understand my error, that is why I could use some help from experts...
Here is the header file.
I tested it with a simple example:
static void foo()
{
std::cout << "foo !!!" << std::endl;
}
int
main( int argc, const char** argv )
{
Functor<void, void> static_func(foo);
static_func();
}
Which gives me this error
/home/test/src/EntryPoint.cpp:237:17: error: no match for call to ‘(Functor<void, void>) ()’
In file included from /home/test/src/EntryPoint.cpp:231:0:
/home/test/include/FunctorTest.h:217:7: note: candidate is:
/home/test/include/FunctorTest.h:292:16: note: Functor<R, TList>::ResultType Functor<R, TList>::operator()(Functor<R, TList>::MyList&&) const [with R = void; TList = {void}; Functor<R, TList>::ResultType = void; Functor<R, TList>::MyList = variadic_typedef<void>]
/home/test/include/FunctorTest.h:292:16: note: candidate expects 1 argument, 0 provided
/home/test/src/EntryPoint.cpp: At global scope:
/home/test/src/EntryPoint.cpp:234:1: warning: unused parameter ‘argc’ [-Wunused-parameter]
/home/test/src/EntryPoint.cpp:234:1: warning: unused parameter ‘argv’ [-Wunused-parameter]
In file included from /home/test/src/EntryPoint.cpp:231:0:
/home/test/include/FunctorTest.h: In instantiation of ‘FunctorHandler<ParentFunctor, Fun>::ResultType FunctorHandler<ParentFunctor, Fun>::operator()(FunctorHandler<ParentFunctor, Fun>::MyList&&) [with ParentFunctor = Functor<void, void>; Fun = void (*)(); FunctorHandler<ParentFunctor, Fun>::ResultType = void; FunctorHandler<ParentFunctor, Fun>::MyList = variadic_typedef<void>]’:
/home/test/src/EntryPoint.cpp:247:1: required from here
/home/test/include/FunctorTest.h:159:49: error: no matching function for call to ‘forward(FunctorHandler<Functor<void, void>, void (*)()>::MyList&)’
/home/test/include/FunctorTest.h:159:49: note: candidates are:
In file included from /usr/include/c++/4.7/bits/stl_pair.h:61:0,
from /usr/include/c++/4.7/utility:72,
from /home/jean/Lib/vitals/include/CLPair.h:28,
from /home/jean/Lib/vitals/include/CLMap.h:27,
from /home/jean/Lib/vitals/include/HTCmdLineParser.h:27,
from /home/test/include/EntryPoint.h:23,
from /home/test/src/EntryPoint.cpp:22:
/usr/include/c++/4.7/bits/move.h:77:5: note: template<class _Tp> constexpr _Tp&& std::forward(typename std::remove_reference<_From>::type&)
/usr/include/c++/4.7/bits/move.h:77:5: note: template argument deduction/substitution failed:
In file included from /home/test/src/EntryPoint.cpp:231:0:
/home/test/include/FunctorTest.h:159:49: note: cannot convert ‘parms’ (type ‘FunctorHandler<Functor<void, void>, void (*)()>::MyList {aka variadic_typedef<void>}’) to type ‘std::remove_reference<convert_in_tuple<variadic_typedef<void> > >::type& {aka convert_in_tuple<variadic_typedef<void> >&}’