I'm not sure if this is related to Error when adapting a class with BOOST_FUSION_ADAPT_ADT ,but even if it is, the question behind it is still not answered/still fails see the comment of the author Error when adapting a class with BOOST_FUSION_ADAPT_ADT . (I used boost 1.69)
I have a struct
#include <string>
#include <cstdint>
namespace rtsp {
struct request {
};
struct response {
uint_fast16_t rtsp_version_major;
uint_fast16_t rtsp_version_minor;
uint_fast16_t status_code;
std::string reason_phrase;
};
using message = boost::variant<request, response>;
}
Which I was able to use with boost::spirit::qi via
BOOST_FUSION_ADAPT_STRUCT(
rtsp::response,
(uint_fast16_t, rtsp_version_major)
(uint_fast16_t, rtsp_version_minor)
(uint_fast16_t, status_code)
(std::string, reason_phrase)
but since I now also want to generate and not only parse the struct it, using boost::spirit::karma I have to use
BOOST_FUSION_ADAPT_ADT(
rtsp::response,
(uint_fast16_t,const uint_fast16_t&,obj.rtsp_version_major, obj.rtsp_version_major = val)
(uint_fast16_t,const uint_fast16_t&,obj.rtsp_version_minor, obj.rtsp_version_minor = val)
(uint_fast16_t,const uint_fast16_t&,obj.status_code, obj.status_code = val)
(std::string,const std::string&,obj.reason_phrase, obj.reason_phrase = val)
)
But it explodes with several errors, from template errors up to syntax errors, for example with
In file included from
/Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/include/rtsp_request.hpp:11:
In file included from
/Users/markus/include/boost/spirit/include/qi.hpp:16: In file included
from /Users/markus/include/boost/spirit/home/qi.hpp:14: In file
included from
/Users/markus/include/boost/spirit/home/qi/action.hpp:14: In file
included from
/Users/markus/include/boost/spirit/home/qi/action/action.hpp:16:
/Users/markus/include/boost/spirit/home/qi/detail/attributes.hpp:153:9:
error: ambiguous partial specializations of
'transform_attribute, unsigned short, boost::spirit::qi::domain, void>'
: transform_attribute
Since it already fails at the #include <boost/spirit/include/qi.hpp> line, maybe someone knows how to correct the BOOST_FUSION_ADAPT_ADT phrase?
Whole code:
/*! #file rtsp_request.hpp
*
*/
#ifndef RTSP_GUI_RTSP_PARSER_HPP
#define RTSP_GUI_RTSP_PARSER_HPP
#include <string>
#include <cstdint>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/fusion/adapted/adt/adapt_adt.hpp>
#include <boost/spirit/include/support_adapt_adt_attributes.hpp>
namespace rtsp {
struct request {
};
struct response {
uint_fast16_t rtsp_version_major;
uint_fast16_t rtsp_version_minor;
uint_fast16_t status_code;
std::string reason_phrase;
};
using message = boost::variant<request, response>;
}
BOOST_FUSION_ADAPT_ADT(
rtsp::response,
(uint_fast16_t,const uint_fast16_t&,obj.rtsp_version_major, obj.rtsp_version_major = val)
(uint_fast16_t,const uint_fast16_t&,obj.rtsp_version_minor, obj.rtsp_version_minor = val)
(uint_fast16_t,const uint_fast16_t&,obj.status_code, obj.status_code = val)
(std::string,const std::string&,obj.reason_phrase, obj.reason_phrase = val)
)
/*
BOOST_FUSION_ADAPT_STRUCT(
rtsp::response,
(uint_fast16_t, rtsp_version_major)
(uint_fast16_t, rtsp_version_minor)
(uint_fast16_t, status_code)
(std::string, reason_phrase)
)*/
namespace rtsp {
template<typename Iterator>
struct rtsp_response_grammar
: ::boost::spirit::qi::grammar<Iterator, response()> {
rtsp_response_grammar() : rtsp_response_grammar::base_type(start) {
namespace ns = ::boost::spirit::standard;
using ::boost::spirit::qi::uint_parser;
using ::boost::spirit::qi::lexeme;
using ::boost::spirit::qi::lit;
using boost::spirit::qi::omit;
using ::boost::spirit::qi::repeat;
quoted_string %= lexeme['"' >> +(ns::char_ - '"') >> '"'];
status_code = uint_parser<uint_fast16_t, 10, 3, 3>();
at_least_one_digit = uint_parser<uint_fast16_t, 10, 1>();
start %= lit("RTSP/") >> at_least_one_digit >> "." >> at_least_one_digit >> omit[+ns::space]
>> status_code >> omit[+ns::space]
>> *(ns::char_ - (lit("\r") | lit("\n")))
>> lit("\r\n");
}
boost::spirit::qi::rule<Iterator, std::string()> quoted_string;
boost::spirit::qi::rule<Iterator, uint_fast16_t()> status_code;
boost::spirit::qi::rule<Iterator, uint_fast16_t()> at_least_one_digit;
boost::spirit::qi::rule<Iterator, response()> start;
};
template <typename OutputIterator>
bool generate_response(OutputIterator sink, const response& reponse)
{
using ::boost::spirit::karma::lit;
return boost::spirit::karma::generate(sink, lit("RTSP/"), reponse);
}
}
#endif //RTSP_GUI_RTSP_PARSER_HPP
/*! #file rtsp_request.cpp
*
*/
#include "rtsp_request.hpp"
#include <iterator>
template
struct rtsp::rtsp_response_grammar<std::string::const_iterator>;
template
bool rtsp::generate_response<std::back_insert_iterator<std::string>>(std::back_insert_iterator<std::string> sink, const response& reponse);
BTW, trying to just use the BOOST_FUSION_ADAPT_STRUCT with the karma generator function would also fail with:
In file included from
/Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/src/rtsp_request.cpp:5:
In file included from
/Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/include/rtsp_request.hpp:11:
In file included from
/Users/markus/include/boost/spirit/include/qi.hpp:16: In file included
from /Users/markus/include/boost/spirit/home/qi.hpp:14: In file
included from
/Users/markus/include/boost/spirit/home/qi/action.hpp:14: In file
included from
/Users/markus/include/boost/spirit/home/qi/action/action.hpp:14: In
file included from
/Users/markus/include/boost/spirit/home/qi/meta_compiler.hpp:15: In
file included from
/Users/markus/include/boost/spirit/home/qi/domain.hpp:18: In file
included from
/Users/markus/include/boost/spirit/home/support/context.hpp:18: In
file included from
/Users/markus/include/boost/spirit/home/support/nonterminal/expand_arg.hpp:20:
/Users/markus/include/boost/spirit/home/support/string_traits.hpp:156:26:
error: implicit instantiation of undefined template
'boost::spirit::traits::char_type_of'
typedef typename char_type_of::type char_type;
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:242:14:
note: in instantiation of template class
'boost::spirit::traits::extract_c_string' requested
here
typename extract_c_string::char_type const*
^ /Users/markus/include/boost/spirit/home/karma/string/lit.hpp:180:21:
note: while substituting deduced template arguments into function
template 'get_c_string' [with String = rtsp::response]
get_c_string(
^ /Users/markus/include/boost/spirit/home/karma/generate.hpp:69:45:
note: in instantiation of function template specialization
'boost::spirit::karma::literal_string::generate
, mpl_::int_<0>, boost::spirit::unused_type>, boost::spirit::context, boost::spirit::locals >, boost::spirit::unused_type, rtsp::response>' requested
here
return compile(expr).generate(sink, context, unused, attr);
^ /Users/markus/include/boost/spirit/home/karma/generate.hpp:91:23:
note: in instantiation of function template specialization
'boost::spirit::karma::generate
, mpl_::int_<0>, boost::proto::exprns_::expr > >, 0>, rtsp::response>'
requested here
return karma::generate(sink, expr, attr);
^ /Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/include/rtsp_request.hpp:69:38:
note: in instantiation of function template specialization
'boost::spirit::karma::generate
, boost::proto::exprns_::expr > >, 0>, rtsp::response>'
requested here
return boost::spirit::karma::generate(sink, lit("RTSP/"), reponse);
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:96:12:
note: template is declared here
struct char_type_of;
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:179:20:
error: no matching function for call to 'call'
return extract_c_string::call(str);
^~~~~~~~~~~~~~~~~~~~~~~~~ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:238:42:
note: in instantiation of member function
'boost::spirit::traits::extract_c_string::call'
requested here
return extract_c_string::call(str);
^ /Users/markus/include/boost/spirit/home/karma/string/lit.hpp:180:21:
note: in instantiation of function template specialization
'boost::spirit::traits::get_c_string' requested
here
get_c_string(
^ /Users/markus/include/boost/spirit/home/karma/generate.hpp:69:45:
note: in instantiation of function template specialization
'boost::spirit::karma::literal_string::generate
, mpl_::int_<0>, boost::spirit::unused_type>, boost::spirit::context, boost::spirit::locals >, boost::spirit::unused_type, rtsp::response>' requested
here
return compile(expr).generate(sink, context, unused, attr);
^ /Users/markus/include/boost/spirit/home/karma/generate.hpp:91:23:
note: in instantiation of function template specialization
'boost::spirit::karma::generate
, mpl_::int_<0>, boost::proto::exprns_::expr > >, 0>, rtsp::response>'
requested here
return karma::generate(sink, expr, attr);
^ /Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/include/rtsp_request.hpp:69:38:
note: in instantiation of function template specialization
'boost::spirit::karma::generate
, boost::proto::exprns_::expr > >, 0>, rtsp::response>'
requested here
return boost::spirit::karma::generate(sink, lit("RTSP/"), reponse);
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:159:25:
note: candidate template ignored: could not match 'T ' against
'rtsp::response'
static T const call (T* str)
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:165:25:
note: candidate template ignored: could not match 'const T ' against
'rtsp::response'
static T const call (T const* str)
A well defined grammar handles either BOOST_FUSION_ADAPT_STRUCT or BOOST_FUSION_ADAPT_ADT without problems.
Example:
namespace rtsp {
...
template <typename OutputIterator>
bool generate_response(OutputIterator sink, const response& reponse)
{
using ::boost::spirit::karma::lit;
using ::boost::spirit::karma::uint_;
using ::boost::spirit::karma::string;
return boost::spirit::karma::generate(sink,
lit("RTSP/") << uint_ << "." << uint_ << " " << uint_ << " " << string
, reponse);
}
}
int main()
{
std::string s;
generate_response(std::back_inserter(s), rtsp::response{ 1, 0, 200, "OK" });
std::cout << s;
}
Prints: RTSP/1.0 200 OK
Update: Qi bug involving BOOST_FUSION_ADAPT_ADT confirmed and reported.
Related
I'm building a custom cMessage that includes vectors as some fields. I have no issue with int or double vectors, but with string vectors, I get an error. Below is a sample message definition to reproduce the issue.
cplusplus {{
#include <vector>
typedef std::vector<int> IntVector;
typedef std::vector<string> StrVector;
}};
class IntVector { #existingClass; };
class StrVector { #existingClass; };
message sampleMessage extends cMessage
{
IntVector SampleIntVector;
StrVector SampleStrVector;
}
In my code, I have the below block
sampleMessage *msg = new sampleMessage();
vector<int> intVect = {1,2};
vector<string> stringVect;
string inputString = "dummy string";
stringVect.push_back(inputString);
msg->setSampleIntVector(intVect);
msg->setSampleStrVector(stringVect);
Using OMNeT++ version 6.0 pre10, at the 7th line, I get the error below suggesting the cMessage is expecting a vector<char *>
error: no viable conversion from 'vector<std::string>' to 'const vector<char *>'
I also tried on OMNeT++ version 5.6.2 and got a different error message. For clarity, the file testModel_m.cc is generated by OMNeT++.
testModel_m.cc:170:13: error: use of overloaded operator '<<' is ambiguous (with operand types 'std::ostream' (aka 'basic_ostream<char>') and 'const std::__cxx11::basic_string<char>')
out << *it;
~~~ ^ ~~~
testModel_m.cc:2040:45: note: in instantiation of function template specialization 'operator<<<std::__cxx11::basic_string<char>, std::allocator<std::__cxx11::basic_string<char> > >' requested here
case 1: {std::stringstream out; out << pp->getSampleStrVector(); return out.str();}
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/basic_string.h:6416:5: note: candidate function [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
operator<<(basic_ostream<_CharT, _Traits>& __os,
^
testModel_m.cc:158:22: note: candidate function [with T = std::__cxx11::basic_string<char>]
inline std::ostream& operator<<(std::ostream& out,const T&) {return out;}
If I change the vector to char *, it works, but for my use case, I would need a vector of string since I search for values in a vector and with char *, that doesn't work quite well.
Is there a way to have a vector<string> field as part of the custom cMessage?
The workaround for the error use of overloaded operator '<<' is ambiguous is adding own definition of operator<< inside cplusplus {{ }} block, for example:
cplusplus {{
#include <vector>
typedef std::vector<int> IntVector;
typedef std::vector<std::string> StrVector;
std::ostream& operator<<(std::ostream &os, const StrVector& vec) {
std::stringstream out;
for (auto i : vec) {
out << i << ", ";
}
return os << out.str();
};
}};
class IntVector { #existingClass; };
class StrVector { #existingClass; };
message sampleMessage {
IntVector SampleIntVector;
StrVector SampleStrVector;
}
I have seen an idiom for using Derived type traits in the base class of a CRTP pattern that looks like this:
template<typename Derived>
struct traits;
template<typename Derived>
struct Base {
using size_type = typename traits<Derived>::size_type;
};
template <typename T>
struct Derived1 : Base<Derived1<T>>{
using size_type = size_t;
void print(){ std::cout << "Derived1" << std::endl; }
};
template <typename T>
struct traits<Derived1<T>> {
using size_type = size_t;
};
int main()
{
using T = float;
Derived1<T> d1;
d1.print();
}
My understanding is that the purpose of the idiom is to delay the instantiation of the Base class's size_type. What I am confused by is the fact that this pattern only seems to work if the derived class is itself templated. For instance, if we change the code to:
template<typename Derived>
struct traits;
template<typename Derived>
struct Base {
using size_type = typename traits<Derived>::size_type;
};
struct Derived1 : Base<Derived1>{
using size_type = size_t;
void print(){ std::cout << "Derived1" << std::endl; }
};
template <>
struct traits<Derived1> {
using size_type = size_t;
};
int main()
{
Derived1 d1;
d1.print();
}
then we get the error
prog.cc: In instantiation of 'struct Base<Derived1>':
prog.cc:21:19: required from here
prog.cc:18:58: error: invalid use of incomplete type 'struct traits<Derived1>'
using size_type = typename traits<Derived>::size_type;
^
prog.cc:14:8: note: declaration of 'struct traits<Derived1>'
struct traits;
^~~~~~
prog.cc: In function 'int main()':
prog.cc:33:9: error: 'Derived1' is not a template
Derived1<float> d1;
Could somebody give me an explanation indicating why the templated derived class compiles, but the untemplated class does not?
The issue you're seeing has nothing to do with CRTP.
Here's what the standard mentions.
If a class template has been declared, but not defined, at the point of instantiation (13.7.4.1),
the instantiation yields an incomplete class type (6.7). [Example:
template<class T> class X; X<char> ch; // error: incomplete type
X<char>
Your traits has only been declared at the point of instantiation of Base<Derived>, hence as per the standard(see above extraction from the standard), struct traits<Derived> yields an incomplete type.
You should reorder the code so that it sees the traits<Derived> specialization when Base<Derived> gets instantiated.
The compilation error you are seeing has nothing to do with CRTP, it's just a bit of a mish-mash of dependencies.
In the code without the templation, your "Base" struct needs the definition of the specialized "traits" struct but it only appears afterwards, so it tries to use the incomplete type it saw in the declaration above.
To get the code to work you need to have the "traits" specialization before the Base declaration, which requires you to also add a declaration of Derived 1, here is a compiling code:
class Derived1;
template<typename Derived>
struct traits;
template <>
struct traits<Derived1> {
using size_type = size_t;
};
template<typename Derived>
struct Base {
using size_type = typename traits<Derived>::size_type;
};
struct Derived1 : Base<Derived1>{
using size_type = size_t;
void print(){ std::cout << "Derived1" << std::endl; }
};
int main()
{
Derived1 d1;
d1.print();
}
I am trying to port some C++ code that encodes the given data into base64 to macos. This is the a code sample from the same source which compiles and executes on godbolt but not on my mac:
#include <vector>
#include <string>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/insert_linebreaks.hpp>
using namespace boost::archive::iterators;
struct Encode
{
/// Sets the contents of an std::string to be used
/// as the input for the encoding operation.
/// \param a_Data The string to encode.
Encode(const std::string& a_Data);
/// Evaluates the expression and performs the base64 encoding.
/// \returns Base64 encoded string.
operator std::string() const;
private:
std::string Evaluate() const;
const char * m_Data;
size_t m_Size;
bool m_LineBreaks;
};
Encode::Encode(const std::string& a_Data)
: m_Data(a_Data.c_str())
, m_Size(a_Data.size())
, m_LineBreaks(false)
{
}
std::string Encode::Evaluate() const
{
typedef base64_from_binary<
transform_width<std::string::const_iterator,6,8>
> iterator;
typedef insert_linebreaks<iterator, 72> linebreak_iterator;
std::string base64;
if (m_LineBreaks) {
base64.assign(
linebreak_iterator(m_Data),
linebreak_iterator(m_Data + m_Size));
}
else {
base64.assign(
iterator(m_Data),
iterator(m_Data + m_Size));
}
return base64;
}
Encode::operator std::string() const
{
return Evaluate();
}
int main()
{
const std::string str64 = Encode("Hello World");
return 0;
}
I am compiling using g++:
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 10.0.0 (clang-1000.10.44.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
However, there is the following error on line iterator(m_Data),:
boost/archive/iterators/transform_width.hpp:112:17: error: calling a private constructor of class 'std::__1::__wrap_iter<const char *>'
super_t(Base(static_cast< T >(start))),
^
<build_path>/boost/archive/iterators/base64_from_binary.hpp:91:13: note: in instantiation of function template specialization 'boost::archive::iterators::transform_width<std::__1::__wrap_iter<const char *>, 6, 8,
char>::transform_width<const char *>' requested here
Base(static_cast< T >(start)),
^
<src_path>/utilsBase64.cc:105:13: note: in instantiation of function template specialization 'boost::archive::iterators::base64_from_binary<boost::archive::iterators::transform_width<std::__1::__wrap_iter<const char *>, 6, 8, char>, char>::base64_from_binary<const
char *>' requested here
iterator(m_Data),
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1420:31: note: declared private here
_LIBCPP_INLINE_VISIBILITY __wrap_iter(iterator_type __x) _NOEXCEPT_DEBUG : __i(__x) {}
^
I have attempted to use clang++ and tried setting -stdlib=libc++ or stdlib=libstdc++ with a similar result. All examples of using the base64_from_binary shown in the code are similar to the given code sample.
One additional detail about m_Data, it is a member variable declared as: const char * m_Data;
Could someone please explain how this can be resolved?
Modifying the code to the following helped fix the issue:
std::string Encode::Evaluate() const
{
typedef base64_from_binary<
transform_width<std::string::const_iterator,6,8>
> iterator;
typedef insert_linebreaks<iterator, 72> linebreak_iterator;
std::string data(m_Data, m_Size);
std::string base64;
if (m_LineBreaks) {
base64.assign(
linebreak_iterator(data.begin()),
linebreak_iterator(data.end()));
}
else {
base64.assign(
iterator(data.begin()),
iterator(data.end()));
}
return base64;
}
I'm having the same error as occurs here. See 'compilation info' at the bottom for the long error message. Basically if I #include <boost/spirit/include/qi.hpp> I can't compile. Totally stumped by this one... I am compiling with g++ 4.9.3 on Debian using C++11. For reference the first part of the error message is:
In file included from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:35:0,
from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
from /usr/include/boost/spirit/home/qi.hpp:20,
from /usr/include/boost/spirit/include/qi.hpp:16,
from prog.cpp:9:
/usr/include/boost/spirit/home/qi/reference.hpp: In instantiation of 'bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >; Context = boost::spirit::context<boost::fusion::cons<std::basic_string<char>&, boost::fusion::nil_>, boost::spirit::locals<> >; Skipper = boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >; Attribute = std::basic_string<char>; Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>]':
My code is actually a little bit different to the above example:
My::Dictionary Parser::parse( const char * s ) {
std::string input(s); // input to parse
qi_my_protocol grammar; // create instance of parser
My::Dictionary msg; // map to receive results
bool result = qi::phrase_parse(
input.begin(),
input.end(),
grammar,
qi::space,
msg
); // returns true if successful
return msg;
}
Your string isn't const, so the iterators first and last aren't const iterators.
Either make the my_Parse function not a template (after all, you hardcode the iterator type on the grammar to be std::string::const_iterator anyways), or instantiate the grammar with the actual iterator type:
my_grammar<Iterator> g;
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <stdio.h>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
template <typename Iterator>
struct my_grammar : boost::spirit::qi::grammar<Iterator, std::string(), ascii::space_type>
{
my_grammar() : my_grammar::base_type(start)
{
using qi::alpha;
using qi::alnum;
start %=
(+alpha | +alnum)
;
}
qi::rule<Iterator, std::string(), ascii::space_type> start;
};
template <typename Iterator>
bool my_Parse(Iterator first, Iterator last, std::string& result)
{
using boost::spirit::ascii::space;
my_grammar<Iterator> g;
bool r = phrase_parse(first, last, g, space, result);
if (!r || first != last) // fail if we did not get a full match
return false;
return r;
}
int main() {
std::string str;
while (getline(std::cin, str))
{
std::string result = "";
if (my_Parse(str.begin(), str.end(), result))
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "got: " << result << std::endl;
std::cout << "\n-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
}
Based on the answer in Detecting constexpr with SFINAE I'm trying to use SFINAE to check if a 'constexpr' is present in my class.
The problem is that the constexpr is a function pointer:
#include <type_traits>
#include <iostream>
typedef int (*ptr_t)();
int bar() { return 9; }
struct Foo {
static constexpr ptr_t ptr = &bar;
};
namespace detail {
template <ptr_t>
struct sfinae_true : std::true_type {};
template <class T>
sfinae_true<T::ptr> check(int);
// Commented out to see why clang was not evaluating to true. This should only be
// a comment when debugging!
// template <class>
// std::false_type check(...);
} // detail::
template <class T>
struct has_constexpr_f : decltype(detail::check<T>(0)) {};
int main(int argc, char *argv[]) {
std::cout << has_constexpr_f<Foo>::value << std::endl;
return 0;
}
It seems to work fine using gcc, but clang complains:
test.cxx:23:39: error: no matching function for call to 'check'
struct has_constexpr_f : decltype(detail::check<T>(0)) {};
^~~~~~~~~~~~~~~~
test.cxx:26:22: note: in instantiation of template class 'has_constexpr_f<Foo>' requested here
std::cout << has_constexpr_f<Foo>::value << std::endl;
^
test.cxx:16:25: note: candidate template ignored: substitution failure [with T = Foo]: non-type template argument for template parameter of pointer type 'ptr_t' (aka 'int (*)()') must have its address taken
sfinae_true<T::ptr> check(int);
~ ^
1 error generated.
Q1: Can anyone suggest a way of doing this which works both for Clang and GCC?
Q2: Is this a bug in gcc, clang or is this left undefined in the c++ standard?
That's not a bug in clang, but an unfortunate restriction of arguments for non-type template parameters of pointer type (see pointer as non-type template argument). Essentially, you can only use arguments of the form &something: [temp.arg.nontype]/1 (from n3797)
[if the template-parameter is a pointer, its argument can be] a constant expression (5.19) that designates the address of a
complete object with static storage duration and external or
internal linkage or a function with external or internal linkage,
including function templates and function template-ids but excluding
non-static class members, expressed (ignoring parentheses) as &
id-expression, where the id-expression is the name of an object or
function, except that the & may be omitted if the name refers to a
function or array and shall be omitted if the corresponding
template-parameter is a reference; or [..]
[emphasis mine]
You can however, use a function pointer in a constant expression that has a non-pointer type, for example a boolean expression such as
T::ptr != nullptr
This works under clang++3.5 and g++4.8.2:
#include <type_traits>
#include <iostream>
typedef int (*ptr_t)();
int bar() { return 9; }
struct Foo0 {
static constexpr ptr_t ptr = &bar;
};
struct Foo1 {
static const ptr_t ptr;
};
ptr_t const Foo1::ptr = &bar;
struct Foo2 {
static const ptr_t ptr;
};
//ptr_t const Foo2::ptr = nullptr;
namespace detail
{
template <bool>
struct sfinae_true : std::true_type {};
template <class T>
sfinae_true<(T::ptr != nullptr)> check(int);
// the result of the comparison does not care
template <class>
std::false_type check(...);
} // detail::
template <class T>
struct has_constexpr_f : decltype(detail::check<T>(0)) {};
int main(int argc, char *argv[]) {
std::cout << std::boolalpha << has_constexpr_f<Foo0>::value << std::endl;
std::cout << std::boolalpha << has_constexpr_f<Foo1>::value << std::endl;
std::cout << std::boolalpha << has_constexpr_f<Foo2>::value << std::endl;
return 0;
}
Note there's a difference between clang++ and g++ for the second output (Foo1): g++ says true, clang++ says false.