Is it possible to name an expression in Boost Spirit without its assignment to a rule?
I know you can name it by assignment to a rule like:
using boost::spirit::standard::char_;
boost::spirit::qi::rule<> number = char_("0") | (char_("1-9") >> *char_("0-9"));
number.name("number");
Which makes debugging of syntax errors easier as you can already have the particular part named in the right way.
But is it possible to do this inline in such a way?
using boost::spirit::standard::char_;
boost::spirit::qi::rule<> twoDigits = char_("0-9") > name(char_("0-9"), "digit");
So that the exception would say that it expected a "digit" at position 2 if it got an input like "3a" (it is not really important here that it is position 2).
The alternative way to express this would be:
using boost::spirit::standard::char_;
boost::spirit::qi::rule<> digit = char_("0-9");
digit.name("digit");
boost::spirit::qi::rule<> twoDigits = digit > digit;
I already checked the source and found out that the expression have a function called what(), which returns a boost::spirit::info object from which the string representation can be retrieved. But I was not able to overwrite that as I am not familier with Boost Proto and the internals of Boost Spirit.
Here is an approach using a custom directive. You can see a very good explanation of how to do something similar (it's a parser, not a directive) here.
The process of creating a custom parser/directive can be divided in four parts:
Defining/creating a terminal to use in a Spirit expression. Usually you'll use BOOST_SPIRIT_TERMINAL unless you need your parser/directive to be of the form parser(whatever)/directive(whatever)[subject]. In this case you'll need to use BOOST_SPIRIT_TERMINAL_EX. This case is specially strange in that you need state associated with your terminal, and so you need to define a struct deriving from terminal<tag::stateful_tag,...>. This is usually not required. I've decided to put all this in namespace custom_directive but you could also put it inside boost::spirit::qi.
Enabling your parser/directive. Here you need to specialize either use_terminal(for parsers) or use_directive(for directives) inside namespace boost::spirit.
Creating the actual parser/directive. This parser/directive requires three things: a attribute<Context,Iterator>::type associated metafunction that states what the attribute of your parser is(in this case I have simply passed the attribute of the subject parser through); a parse member function with the appropiate signature that does the real parsing (again I have deferred to the subject parser), and a what member function that is what we really are interested in modifying that returns whatever you have associated with the terminal on construction. Again I have decided to use namespace custom_directive but you could also put it inside boost::spirit::qi.
Connecting the terminal with your actual parser/directive. This needs to be inside boost::spirit::qi and requires that you specialize either make_directive or make_primitive with your terminal tag and instatiate your actual parser/directive.
Live on coliru
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
//START OF expression_renamer.hpp
namespace custom_directive
{
BOOST_SPIRIT_TERMINAL(rename_expression);
struct expression_renamer: boost::spirit::terminal<boost::spirit::tag::stateful_tag<std::string, tag::rename_expression> >
{
typedef boost::spirit::tag::stateful_tag<std::string, tag::rename_expression> tag_type;
expression_renamer(std::string const& p) : boost::spirit::terminal<tag_type>(p) {}
};
}
namespace boost { namespace spirit
{
template <>
struct use_directive<qi::domain, boost::spirit::tag::stateful_tag<std::string, custom_directive::tag::rename_expression> > // enables expression_renamer[p]
: mpl::true_ {};
}}
namespace custom_directive
{
template <typename Subject, typename Data>
struct rename_directive : boost::spirit::qi::unary_parser<rename_directive<Subject,Data> >
{
typedef Subject subject_type;
rename_directive(Subject const& subject_, Data const& data_)
: subject(subject_),data(data_) {}
template <typename Context, typename Iterator>
struct attribute
{
typedef typename
boost::spirit::traits::attribute_of<subject_type, Context, Iterator>::type
type;
};
template <typename Iterator, typename Context
, typename Skipper, typename Attribute>
bool parse(Iterator& first, Iterator const& last, Context& context, Skipper const& skipper, Attribute& attr_) const
{
return subject.parse(first, last, context, skipper, attr_);
}
template <typename Context>
boost::spirit::info what(Context& context) const
{
return boost::spirit::info(data);
}
Subject subject;
Data data;
};
}
// instantiation of the parser
namespace boost { namespace spirit { namespace qi
{
template<typename Data, typename Subject,typename Modifiers>
struct make_directive<tag::stateful_tag<Data, custom_directive::tag::rename_expression>, Subject, Modifiers>
{
typedef custom_directive::rename_directive<Subject,Data> result_type;
template<typename Terminal>
result_type operator()(Terminal& term, Subject const& subject, unused_type) const
{
typedef tag::stateful_tag<Data,
custom_directive::tag::rename_expression> tag_type;
using spirit::detail::get_stateful_data;
return result_type(subject,get_stateful_data<tag_type>::call(term));
}
};
}}}
//END OF expression_renamer.hpp
template <typename Parser>
void parse(std::string const& str, Parser const& parser)
{
std::cout << "Parsing: \"" << str << "\"" << " with `digit` `point` `digit`" << std::endl;
std::string::const_iterator iter=str.begin(),end=str.end();
boost::spirit::qi::parse(iter,end,parser);
}
int main()
{
custom_directive::expression_renamer point("point");
custom_directive::expression_renamer digit("digit");
boost::spirit::qi::char_type char_;
boost::spirit::qi::rule<std::string::const_iterator> twoDigitsWithPoint = char_("0-9") > point[char_('.')] > digit[char_("0-9")];
boost::spirit::qi::on_error<boost::spirit::qi::fail>
(
twoDigitsWithPoint
, std::cout
<< boost::phoenix::val("Error! Expecting ")
<< boost::spirit::qi::_4 // what failed?
<< std::endl
);
parse("33",twoDigitsWithPoint);
parse("3.a",twoDigitsWithPoint);
}
You can group using auto. But debug names can only be attached to the non-terminals: qi::rule<> and qi::grammar<> (other "groupings" aren't traced anyways, not even with attr_cast<>, which is semantically close to the attribute propagation semantics of a rule<>).
There is a big caveat related to using auto:
Proto expression trees need to be deep copied. Recent Qi defines qi::copy() to do just that.
Related
I'm writing an extensible library where it has become convenient to overload STL's to_string() for custom types. For that I've designed a generic overload template that throws an exception if not specialized:
namespace std {
// ...
template < typename T >
inline std::string to_string(const T& in, const std::string& separator = ",") {
throw std::runtime_error("invalid call to " + std::string(__func__) + "(): missing template specialization for type " + typeid(T).name());
}
} // namespace std
This is useful mainly because the description will provide a clear explanation on the issue and how to solve it, and avoids having to use polymorphism to implement derived implementations (the function is only marginally/optionally required for certain applications such as serialization, I/O, etc.).
However, the issue with this approach is that the overload template will be deduced even with types where <string> already provides an overload for.
My question is if is there a way to force the non-template overload to be used only when there is no non-template definition available?
I recommend that you do not generate a runtime exception for something that should be a compilation failure.
It could look like this:
#include <string>
#include <type_traits>
namespace extra {
template <class T>
inline std::string to_string(const T& in) {
static_assert(std::is_arithmetic_v<T>, "T needs extra::to_string overload");
return std::to_string(in);
}
} // namespace extra
... and then you don't need to check if it's an arithmetic type at the call site:
template <class T>
void func(const T& arg) {
std::cout << extra::to_string(arg);
}
Demo
I ended up declaring to_string on a different namespace, and made use of type traits to delegate basic types towards STL's std::to_string:
namespace extra {
template < typename T >
struct invalid : std::false_type { /* ... */ };
template < typename T >
inline std::string to_string(const T& in) {
// static_assert(invalid< iT >::value, "Invalid call to extra::to_string(): missing template specialization for required type!"); // never compiles
throw std::runtime_error("Invalid call to extra::_to_string(): missing template specialization for required types[" + std::string(typeid(T).name()) + "]!");
}
} // namespace extra
template < typename T >
void func(const T& arg) {
// ...
if constexpr (std::is_arithmetic< T >()) {
std::cout << std::to_string(arg);
} else {
std::cout << extra::to_string(arg);
}
// ...
}
Although I am still trying to figure out how to proper write the static assertion in order to generate the error during compilation, at this stage this behaves how I needed it to.
I am implementing a "starts_with" function to check if a string starts with some prefix. I want the function to be able to compare std::string and std::string_view interchangeably. The issue I'm running into is when a std::string is passed as an argument I want it to be passed by reference and a std::string_view to be passed by value.
Currently I have this setup:
#include <string_view>
#include <utility>
template <typename String>
struct string_type {
using type = const String&;
};
template <>
struct string_type<std::string_view> {
using type = const std::string_view;
};
template <typename String>
using string_type_t = typename string_type<String>::type;
template <typename String, typename Prefix>
bool string_starts_with(string_type_t<String> str, string_type_t<Prefix> pre) {
if (pre.length() > str.length()) {
return false;
}
for (auto ch = std::pair{str.begin(), pre.begin()};
ch.second != pre.end();
++ch.first, ++ch.second) {
if (*ch.first != *ch.second) {
return false;
}
}
return true;
}
int main() {
using namespace std::string_view_literals;
return string_starts_with("hello"sv, "hel"sv) ? 0 : 1;
}
However gcc and clang (tested here) are unable to deduce the template parameters and I have to specify the types explicitly string_starts_with<std::string_view, std::string_view>(..., ...).
One obvious solution would be to provide overloads for std::string_view but then I need to implement 4 different functions with essentially the same body (string_starts_with(std::string, std::string), string_starts_with(std::string, std::string_view), string_starts_with(std::string_view, std::string_view), string_starts_with(std::string_view, std::string)). This might still be manageable but what if there is another string-like object such as std::vector<char> or std::array<char> that I want to introduce to the API it just becomes unmanageable.
Nesting a type-aliase disables deduction. Because the compiler has no way to guess which class the nested parameter is possibly defined in. You can define a traits template and/or use meta-programming constructs (enable_if, if constexpr, concept/requires, static_assert...) to constraint the template:
template<typename >
struct str_ref_traits: std::false_type{};
template<>
struct str_ref_traits<std::string&>: std::true_type{};
template<>
struct str_ref_traits<std::string_view>: std::true_type{};
template <typename S, typename P>
bool string_starts_with(S str, P pre) {
static_assert(str_ref_traits<S>::value);
static_assert(str_ref_traits<P>::value);
if (pre.length() > str.length())
return false;
return std::equal(begin(pre), end(pre), begin(str));
};
Of course as mentioned in the comments, std::string_view can handle std::string - by design. But I wanted to mention the general rule that nested type-aliase cannot be deduced.
In fact, the design goal of std::type_identity is to be used as a deduction disabler on purpose.
regards,
FM.
I'm trying to build a utility ToString function that either calls std::to_string or a custom to_string method defined somewhere else. This is what I came up with for version 1.0:
Context
The code here is a close approximation to what I'm working with for context. I have enums from a 3rd party library that are defined using the C stlye definition, as well as enums I've defined using the C++ style.
//From my 3rd party library
namespace ThirdPartyNamespace
{
typedef enum
{
UnscopedValue
} Unscoped;
}
//Defined in Scoped.h
namespace MyNamespace
{
enum class Scoped
{
ScopedValue
};
static std::string to_string(Scoped value)
{
return "Enums::Scoped";
}
}
//Defined in Helpers.h, contains to_string methods for the enums in the 3rd party library
namespace Helpers
{
static std::string to_string(ThirdPartyNamespace::Unscoped value)
{
return "Enums::Unscoped";
}
}
Calling Code
ThirdPartyNamespace::Unscoped x = ThirdPartyNamespace::UnscopedValue;
MyNamespace::Scoped y = MyNamespace::Scoped::ScopedValue;
std::cout << Utilities::ToString(x) << std::endl;
std::cout << Utilities::ToString(y) << std::endl;
Utility Code
namespace Utilities
{
template <typename T>
std::string ToString(const T& value)
{
using std::to_string;
return to_string(value);
}
}
This compiles and works for Scoped but writes the integer value for Unscoped. I did some research and it seemed like to fix this I would need to use std::enable_if for my enum types. After some research on how std::enable_if works and SFINAE I came up with what I thought would work:
Utility Code
template<typename T, std::enable_if_t<!std::is_enum<T>::value>* = nullptr>
static std::string ToString(const T& value)
{
using std::to_string;
return to_string(value);
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
static std::string ToString(const T& value)
{
return to_string(value);
}
However, this does not compile. Specifically for Unscoped it gives throws a 'to_string': identifier not found error (I verified that was the error by commenting out the call with Unscoped and it compiled and worked as expected).
My question is, why does the compiler fail to find my custom to_string method?
A bonus question: From my reading I found that ::type* = nullptr "is setting a default value to the template 'type' parameter equal to 'nullptr'"(Source), what exactly does that mean and why would one want a default value here?
You need either
move
std::string to_string(ThirdPartyNamespace::Unscoped value)
into namespace ThirdPartyNamespace to be found thanks to ADL
or, change your ToString to
template <typename T>
std::string ToString(const T& value)
{
using std::to_string;
using Helpers::to_string;
return to_string(value);
}
I want to generate some formatted output. For this some indention is needed. So at some point during generation I would like to get the current position, to have the following lines indented with that amount.
Here is a minimal example. Please assume, that we don't know how long the output of karma::lit("Some text: ") is during compile time. In fact, this leading text may be generated by several rules.
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
using namespace std;
int main() {
vector<int> v { 0, 1, 2, 3 };
{
namespace karma = boost::spirit::karma;
karma::rule<ostream_iterator<char>, std::vector<int>() > myRule =
karma::lit("Some text: ") << (karma::int_ % karma::eol);
karma::generate(ostream_iterator<char>(cout), myRule, v);
}
return 0;
}
This produces
Some text: 0
1
2
3
I would like the result:
Some text: 0
1
2
3
To achieve this, one needs to know the current position, right before the vector gets generated. So, something like an equivalent for qi::raw[]?
Update: A pointer to the up to this point generated output, would also do.
I believe this approach is similar to the one you described in the comments. It assumes that the only information you can get from the iterator is the total count of characters written. It could be simplified further if you had access to the current column by modifying the header files as mentioned in the other answer.
Edit: Modified the code with the approach Mike M suggested in the comments. Now it has a better interface. Tested with g++ 4.8.1 and clang 3.2 using boost 1.54.0.
In order to use you need to first define two terminals of type position_getter:
std::size_t start=0, end=0;
position_getter start_(start), end_(end);
Then you simply put start_ at the start of a line, and end_ at the point where you want to know in which column you are. After that you can use end - start to calculate that column. Since this calculation needs to be done at parse time (not compile time) you need to use phx::ref(end) - phx::ref(start).
With the modifications mentioned in the other answer, you could simply define one terminal:
std::size_t column=0;
position_getter column_(column);
And then use it in rule like this:
myRule = karma::lit("Some text: ")
<< column_
<< karma::int_ %
(karma::eol << karma::repeat(phx::ref(column))[karma::char_(" ")]);
#include <iostream>
#include <string>
#include <vector>
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
//START OF CURRENT_POS.HPP
#include <boost/spirit/include/karma_generate.hpp>
///////////////////////////////////////////////////////////////////////////////
// definition the place holder
namespace custom_generator {
BOOST_SPIRIT_TERMINAL_EX(current_pos);
struct position_getter: boost::spirit::terminal<
boost::spirit::tag::stateful_tag<std::size_t&, tag::current_pos> > {
typedef boost::spirit::tag::stateful_tag<std::size_t&, tag::current_pos> tag_type;
position_getter(std::size_t& p)
: boost::spirit::terminal<tag_type>(p) {
}
};
}
///////////////////////////////////////////////////////////////////////////////
// implementation the enabler
namespace boost {
namespace spirit {
// enables a terminal of type position_getter
template<>
struct use_terminal<karma::domain,
tag::stateful_tag<std::size_t&, custom_generator::tag::current_pos> > : mpl::true_ {
};
}
}
///////////////////////////////////////////////////////////////////////////////
// implementation of the generator
namespace custom_generator {
struct current_pos_generator: boost::spirit::karma::primitive_generator<
current_pos_generator> {
current_pos_generator(std::size_t& pos_)
: pos(pos_) {
}
// Define required output iterator properties
typedef typename boost::mpl::int_<
boost::spirit::karma::generator_properties::tracking> properties;
// Define the attribute type exposed by this parser component
template<typename Context, typename Unused>
struct attribute {
typedef boost::spirit::unused_type type;
};
// This function is called during the actual output generation process.
// It stores information about the position in the output stream in
// the variable you used to construct position_getter
template<typename OutputIterator, typename Context, typename Delimiter,
typename Attribute>
bool generate(OutputIterator& sink, Context& ctx,
Delimiter const& delimiter, Attribute const& attr) const {
std::size_t column = sink.get_out_count();
// This would only work if you comment "private:" in line 82 of
// boost/spirit/home/karma/detail/output_iterator.hpp
// std::size_t column = sink.track_position_data.get_column()-1;
pos = column;
return true;
}
// This function is called during error handling to create
// a human readable string for the error context.
template<typename Context>
boost::spirit::info what(Context& ctx) const {
return boost::spirit::info("current_pos");
}
std::size_t& pos;
};
}
///////////////////////////////////////////////////////////////////////////////
// instantiation of the generator
namespace boost {
namespace spirit {
namespace karma {
template<typename Modifiers>
struct make_primitive<
tag::stateful_tag<std::size_t&, custom_generator::tag::current_pos>,
Modifiers> {
typedef custom_generator::current_pos_generator result_type;
template<typename Terminal>
result_type operator()(Terminal& term, unused_type) const {
typedef tag::stateful_tag<std::size_t&,
custom_generator::tag::current_pos> tag_type;
using spirit::detail::get_stateful_data;
return result_type(get_stateful_data<tag_type>::call(term));
}
};
}
}
}
//END OF CURRENT_POS.HPP
int main() {
std::vector<int> v { 0, 1, 2, 3 };
{
namespace karma = boost::spirit::karma;
namespace phx = boost::phoenix;
using custom_generator::position_getter;
std::size_t start = 0, end = 0;
position_getter start_(start), end_(end);
karma::rule<std::ostream_iterator<char>, std::vector<int>()> myRule =
start_
<< karma::lit("Some text: ")
<< end_
<< karma::int_ % (karma::eol
<< karma::repeat(phx::ref(end) - phx::ref(start))[karma::char_(
" ")]);
karma::generate(std::ostream_iterator<char>(std::cout), myRule, v);
std::cout << std::endl;
karma::rule<std::ostream_iterator<char>, std::vector<int>()> myRuleThatAlsoWorks =
karma::lit(":)")
<< karma::eol
<< start_
<< karma::lit("Some text: ")
<< end_
<< karma::int_ % (karma::eol
<< karma::repeat(phx::ref(end) - phx::ref(start))[karma::char_(
" ")]);
karma::generate(std::ostream_iterator<char>(std::cout), myRuleThatAlsoWorks,
v);
}
return 0;
}
Here is a custom directive heavily based on the explanations here.
Unfortunately due to the fact that the information you need is contained in a private member of the iterator, this only works with the really simple example that you posted. If you output anything else before everythings gets misaligned. You can work around this if you are willing to modify slightly the code in detail/output_iterator.hpp. You can either comment the "private:" in position_policy or simply add a member function get_out_column in the same vein as get_out_count.
In order to use it you need to change your:
karma::int_ % karma::eol;
to:
custom_generator::align_list_to_current_position[karma::int_];
As you can see the custom directive requires a lot of boilerplate but big part of this code is common to every directive. In fact, besides changing the names, I have only needed to change three things:
Make sure that tracking is in the set of required properties:
typedef typename boost::mpl::int_<
Subject::properties::value | karma::generator_properties::tracking
> properties;
Make the attribute of the directive be the same as the one a list(%) would have(by looking here):
template <typename Context, typename Iterator>
struct attribute
: boost::spirit::traits::build_std_vector<
typename boost::spirit::traits::attribute_of<Subject, Context, Iterator>::type
>
{};
And finally change the generate function. In this function I simply build a list that has as its left member whatever you passed to the directive and as its right one the concatenation of karma::eol and as many spaces are as needed to be aligned.
#include <iostream>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
//START OF ALIGN_LIST_TO_CURRENT_POSITION.HPP
#include <boost/spirit/include/karma_generate.hpp>
///////////////////////////////////////////////////////////////////////////////
// definition the place holder
namespace custom_generator
{
BOOST_SPIRIT_TERMINAL(align_list_to_current_position);
}
///////////////////////////////////////////////////////////////////////////////
// implementation the enabler
namespace boost { namespace spirit
{
// We want custom_generator::align_list_to_current_position to be usable as a directive only,
// and only for generator expressions (karma::domain).
template <>
struct use_directive<karma::domain, custom_generator::tag::align_list_to_current_position>
: mpl::true_ {};
}}
///////////////////////////////////////////////////////////////////////////////
// implementation of the generator
namespace custom_generator
{
// That's the actual columns generator
template <typename Subject>
struct align_list_to_current_position_generator
: boost::spirit::karma::unary_generator<
align_list_to_current_position_generator<Subject> >
{
// Define required output iterator properties: take the properties needed by the subject and add tracking
typedef typename boost::mpl::int_<Subject::properties::value | boost::spirit::karma::generator_properties::tracking> properties;
// Define the attribute type exposed by this parser component
template <typename Context, typename Iterator>
struct attribute
: boost::spirit::traits::build_std_vector<
typename boost::spirit::traits::attribute_of<Subject, Context, Iterator>::type>
{};
align_list_to_current_position_generator(Subject const& s)
: subject(s)
{}
// This function is called during the actual output generation process.
// It dispatches to the embedded generator while supplying a new
// delimiter to use
template <typename OutputIterator, typename Context
, typename Delimiter, typename Attribute>
bool generate(OutputIterator& sink, Context& ctx
, Delimiter const& delimiter, Attribute const& attr) const
{
using boost::spirit::karma::repeat;
using boost::spirit::karma::char_;
using boost::spirit::karma::eol;
using boost::spirit::karma::domain;
std::size_t column = sink.get_out_count();
//This would only work if you comment "private:" in line 82 of boost/spirit/home/karma/detail/output_iterator.hpp
// std::size_t column = sink.track_position_data.get_column()-1;
return boost::spirit::compile<domain>(subject%(eol << repeat(column)[char_(" ")])).generate(sink, ctx, delimiter, attr);
}
// This function is called during error handling to create
// a human readable string for the error context.
template <typename Context>
boost::spirit::info what(Context& ctx) const
{
return boost::spirit::info("align_list_to_current_position", subject.what(ctx));
}
Subject subject;
};
}
///////////////////////////////////////////////////////////////////////////////
// instantiation of the generator
namespace boost { namespace spirit { namespace karma
{
// This is the factory function object invoked in order to create
// an instance of our align_list_to_current_position_generator.
template <typename Subject, typename Modifiers>
struct make_directive<custom_generator::tag::align_list_to_current_position, Subject, Modifiers>
{
typedef custom_generator::align_list_to_current_position_generator<Subject> result_type;
result_type operator()(unused_type, Subject const& s, unused_type) const
{
return result_type(s);
}
};
}}}
//END OF ALIGN_LIST_TO_CURRENT_POSITION.HPP
int main() {
std::vector<int> v { 0, 1, 2, 3 };
{
namespace karma = boost::spirit::karma;
using custom_generator::align_list_to_current_position;
karma::rule<std::ostream_iterator<char>, std::vector<int>() > myRule =
karma::lit("Some text: ") << align_list_to_current_position[karma::int_];
karma::generate(std::ostream_iterator<char>(std::cout), myRule, v);
std::cout << std::endl;
//This rule would work if you make the changes mentioned in align_list_to_current_position_generator::generate
karma::rule<std::ostream_iterator<char>, std::vector<int>() > myRuleThatFails =
karma::lit(":_(") << karma::eol << karma::lit("Some text: ") << align_list_to_current_position[karma::int_ << karma::int_];
karma::generate(std::ostream_iterator<char>(std::cout), myRuleThatFails, v);
}
return 0;
}
Generally, I would use boost::mpl::for_each<>() to traverse a boost::mpl::vector, but this requires a functor with a template function declared like the following:
template<typename T> void operator()(T&){T::staticCall();}
My problem with this is that I don't want the object T to be instantiated by for_each<>. I don't need the T parameter in the operator() at all. Is there a way to accomplish this, or an alternative to for_each<> that doesn't pass an object of type T to the template function?
Optimally, I would like the operator() definition to look like this:
template<typename T> void operator()(){T::staticCall();}
And of course, I don't want T to be instantiated at all prior to the call. Any other tips/suggestions are also welcome.
Just encountered the same situation and provided different solution to the problem which I would like to share. It's not as that obvious, but it uses an existing algorithm. The idea is to use pointers instead.
typedef boost::mpl::vector<type1*, type2*> container;
struct functor
{
template<typename T> void operator()(T*)
{
std::cout << "created " << typeid(T).name() << std::endl;
}
};
int main()
{
boost::mpl::for_each<container>(functor());
}
so here we get a null pointers, but we don't care as we are not going to use them.
As I stated before that is not obvious in the code and would probably require some additional comments, but it still solves the question without writing any additional code.
added
I think Diego Sevilla suggested something similar.
Interesting question! As far as I can tell, Boost.MPL does not seem to provide such an algorithm. However, writing your own should not be too difficult using iterators.
Here is a possible solution:
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/next_prior.hpp>
#include <boost/mpl/vector.hpp>
using namespace boost::mpl;
namespace detail {
template < typename Begin, typename End, typename F >
struct static_for_each
{
static void call( )
{
typedef typename Begin::type currentType;
F::template call< currentType >();
static_for_each< typename next< Begin >::type, End, F >::call();
}
};
template < typename End, typename F >
struct static_for_each< End, End, F >
{
static void call( )
{
}
};
} // namespace detail
template < typename Sequence, typename F >
void static_for_each( )
{
typedef typename begin< Sequence >::type begin;
typedef typename end< Sequence >::type end;
detail::static_for_each< begin, end, F >::call();
}
[The naming may not be very well chosen, but well...]
Here is how you would use this algorithm:
struct Foo
{
static void staticMemberFunction( )
{
std::cout << "Foo";
}
};
struct Bar
{
static void staticMemberFunction( )
{
std::cout << "Bar";
}
};
struct CallStaticMemberFunction
{
template < typename T >
static void call()
{
T::staticMemberFunction();
}
};
int main()
{
typedef vector< Foo, Bar > sequence;
static_for_each< sequence, CallStaticMemberFunction >(); // prints "FooBar"
}
Well, first of all, the static call in your code means that your object will exist. Before/after is meaningless in that regard. The only time, "I don't want T to be instantiated at all prior to the call," make sense is when T is a template. It's not, because it can't be. It is true that it is that line that causes the object to exist, but I am pretty sure it won't just exist there once the product is compiled.
Second of all, I don't believe that there's a current method to use for_each without instantiating. IMHO this is a bug in MPL caused by a questionable decision to use operator(). Won't say it's wrong since I know the developer and he's a lot smarter than I am, but it seems so from here now that you bring this up.
So, I think you're stuck having to remake a for_each that calls a templated function that doesn't require the parameter. I'm almost certain it is possible, but also equally certain it's not readily available as a premade component in MPL.
Marcin, you're very right. I've been thinking this along and I don't see an easy solution for this. Even if you cannot write the empty operator(), it would be at least possible to use a pointer, that doesn't need an actual object to exist. You have to roll your own implementation, it seems.
Here is an alternative solution highly inspired from Luc Touraille's answer.
This version is done using Metafunction classes instead of functions which allows the static_for_each to be called even outside of function scopes (useful if the job has to be totally done at compiletime so you have no unnecessary functions called at runtime).
Furthermore it gives more interaction thanks to the first and last typedefs, allowing to get information out of the loop if necessary, a little like the way a return works for a function.
You can also access the previous iteration result within each iteration thanks to the second template parameter Previous passed to the metafunction class F.
Finally you can provide data to the loop process using the Initial template parameter, it will be given as the value of the Previous parameter of the first iteration.
# include <boost/mpl/begin_end.hpp>
# include <boost/mpl/next_prior.hpp>
# include <boost/mpl/apply.hpp>
namespace detail_static_for_each
{
// Loop
template<typename Begin, typename End, typename F, typename Previous>
struct static_for_each
{
private:
typedef typename Begin::type current_type;
public:
typedef typename boost::mpl::apply<F, current_type, Previous>::type first;
typedef typename static_for_each<typename boost::mpl::next<Begin>::type, End, F, first>::last last;
};
// End of loop
template<typename End, typename F, typename Last>
struct static_for_each<End, End, F, Last>
{
public:
typedef Last first;
typedef Last last;
};
} // namespace detail_static_for_each
// Public interface
template<typename Sequence, typename F, typename Initial = void>
struct static_for_each
{
private:
typedef typename boost::mpl::begin<Sequence>::type begin;
typedef typename boost::mpl::end<Sequence>::type end;
typedef typename detail_static_for_each::static_for_each<begin, end, F, Initial> loop;
public:
typedef typename loop::first first;
typedef typename loop::last last;
};
Here is a simple example that both gives and retrieves data :
# include <iostream>
# include <boost/type_traits/is_same.hpp>
# include <boost/mpl/if.hpp>
# include <boost/mpl/vector.hpp>
# include "static_for_each.hpp"
struct is_there_a_float
{
template<typename currentItem, typename PreviousIterationType>
struct apply
{
typedef typename boost::mpl::if_< PreviousIterationType,
PreviousIterationType,
boost::is_same<float, currentItem> >::type type;
};
};
struct test
{
typedef boost::mpl::vector< char, long, long, double, float, int, char > sequence;
typedef static_for_each<sequence, is_there_a_float, boost::false_type>::last found;
};
int main(void)
{
std::cout << std::boolalpha << test::found::value << std::endl;
return (0);
}
These features makes the use of static_for_each more similar to the use of common runtime loops (while, for, BOOST_FOREACH ...) as you can interact more directly with the loop.
I liked (up-voted) the solution with pointer and own defined *_for_each function. Here is an alternative using type wrapper for T if the goal is to avoid object creation till it is needed.
template<typename T>
struct Wrapper
{
typedef T type;
};
struct Functor
{
template<typename T> void operator()(T t)
{
T::type obj(1);
T::type::static_fuc();
}
};
struct T1
{
T1(int a) : m_a(a) { }
int m_a;
static inline void static_fuc() { }
};
struct T2
{
T2(int a) : m_a(a) { }
int m_a;
static inline void static_fuc() { }
};
void fun()
{
namespace mpl=boost::mpl;
typedef mpl::vector<Wrapper<T1>,Wrapper<T2> > t_vec;
mpl::for_each<t_vec>(Functor());
}