Hi I'm using boost::pfr for basic reflection, it works fine, but the problem is it is only print or deal with the field values, like with boost::pfr::io it prints each member of the struct, but how can I print it as name value pairs, same issue with for_each_field, the functor only accepts values, but not names. How can I get the field names?
struct S {
int n;
std::string name;
};
S o{1, "foo"};
std::cout << boost::pfr::io(o);
// Outputs: {1, "foo"}, how can I get n = 1, name = "foo"?
If you think adapting a struct is not too intrusive (it doesn't change your existing definitions, and you don't even need to have it in a public header):
BOOST_FUSION_ADAPT_STRUCT(S, n, name)
Then you can concoct a general operator<< for sequences:
namespace BF = boost::fusion;
template <typename T,
typename Enable = std::enable_if_t<
// BF::traits::is_sequence<T>::type::value>
std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
std::ostream& operator<<(std::ostream& os, T const& v)
{
bool first = true;
auto visitor = [&]<size_t I>() {
os << (std::exchange(first, false) ? "" : ", ")
<< BF::extension::struct_member_name<T, I>::call()
<< " = " << BF::at_c<I>(v);
};
// visit members
[&]<size_t... II>(std::index_sequence<II...>)
{
return ((visitor.template operator()<II>(), ...);
}
(std::make_index_sequence<BF::result_of::size<T>::type::value>{});
return os;
}
(Prior to c++20 this would require some explicit template types instead of the lambdas, perhaps making it more readable. I guess I'm lazy...)
Here's a live demo: Live On Compiler Explorer
n = 1, name = foo
Bonus: Correctly quoting string-like types
Live On Compiler Explorer
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <iostream>
#include <iomanip>
namespace MyLib {
struct S {
int n;
std::string name;
};
namespace BF = boost::fusion;
static auto inline pretty(std::string_view sv) { return std::quoted(sv); }
template <typename T,
typename Enable = std::enable_if_t<
not std::is_constructible_v<std::string_view, T const&>>>
static inline T const& pretty(T const& v)
{
return v;
}
template <typename T,
typename Enable = std::enable_if_t<
// BF::traits::is_sequence<T>::type::value>
std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
std::ostream& operator<<(std::ostream& os, T const& v)
{
bool first = true;
auto visitor = [&]<size_t I>() {
os << (std::exchange(first, false) ? "" : ", ")
<< BF::extension::struct_member_name<T, I>::call()
<< " = " << pretty(BF::at_c<I>(v));
};
// visit members
[&]<size_t... II>(std::index_sequence<II...>)
{
return (visitor.template operator()<II>(), ...);
}
(std::make_index_sequence<BF::result_of::size<T>::type::value>{});
return os;
}
} // namespace MyLib
BOOST_FUSION_ADAPT_STRUCT(MyLib::S, n, name)
int main()
{
MyLib::S o{1, "foo"};
std::cout << o << "\n";
}
Outputs:
n = 1, name = "foo"
The library cannot offer any such functionality because it is currently impossible to obtain the name of a member of a class as value of an object.
If you want to output field names, you need to declare string objects mapped with the members and implement a operator<< which uses these strings manually.
To do this a more sophisticated reflection library would probably offer macros to use in the definition of the members. Macros can expand their argument(s) into a declaration using the provided name as identifier while also producing code using the name as string literal (via the # macro replacement operator).
It's stupid but hey, with a stringifying macro per field it could be enough for you.
C++14, no additional library
#include <boost/pfr.hpp>
struct S
{
int n;
std::string name;
static char const* const s_memNames[2];
};
char const* const S::s_memNames[2] = {"n", "name"};
// utility
template< size_t I, typename TR >
char const* MemberName()
{
using T = std::remove_reference_t<TR>;
if (I < std::size(T::s_memNames))
return T::s_memNames[I];
return nullptr;
}
// test:
#include <iostream>
using std::cout;
template< size_t I, typename T >
void StreamAt(T&& inst)
{
char const* n = MemberName<I,T>();
auto& v = boost::pfr::get<I>(inst);
cout << "(" << n << " = " << v << ")";
}
int main()
{
S s{2, "boo"};
boost::pfr::for_each_field(s, [&](const auto&, auto I)
{
StreamAt<decltype(I)::value>(s);
cout << "\n";
});
}
output:
(n = 2)
(name = boo)
(previous version of the suggestion, this one has more fluff so less interesting)
#include <boost/pfr.hpp>
// library additions:
static char const* g_names[100];
template< size_t V >
struct Id : std::integral_constant<size_t, V > {};
template< size_t I, typename T >
using TypeAt = boost::pfr::tuple_element_t<I, T>;
template<std::size_t Pos, class Struct>
constexpr int Ni() // name index
{
return std::tuple_element_t<Pos, typename std::remove_reference_t<Struct>::NamesAt >::value;
}
struct StaticCaller
{
template< typename Functor >
StaticCaller(Functor f) { f();}
};
///
/// YOUR CODE HERE
struct S
{
using NamesAt = std::tuple<Id<__COUNTER__>, Id<__COUNTER__>>; // add this
int n;
std::string name;
static void Init() // add this
{
g_names[Ni<0,S>()] = "n";
g_names[Ni<1,S>()] = "name";
}
};
StaticCaller g_sc__LINE__(S::Init); // add this
// utilities
template< size_t I, typename T >
auto GetValueName(T&& inst)
{
return std::make_pair(boost::pfr::get<I>(inst), g_names[Ni<I,T>()]);
}
// test:
#include <iostream>
using std::cout;
template< size_t I, typename T >
void StreamAt(T&& inst)
{
auto const& [v,n] = GetValueName<I>(inst);
cout << "(" << v << ", " << n << ")";
}
int main()
{
S s{2, "boo"};
boost::pfr::for_each_field(s, [&](const auto&, auto I)
{
StreamAt<decltype(I)::value>(s);
cout << "\n";
});
}
output
(2, n)
(boo, name)
Related
I am building a larger state machine in boost sml for the first time and need a way to visualize (e.g. export to graphviz) the whole state machine. Any idea how one could do that? Is there any way to iterate over the structure of the state machine and print it?
Disclaimer: I have zero experience with SML (had to find where it lives).
First Attempt, Mmmm
However, trying some things with the visit_current_states interface, I came up with this ... not so nice implementation that maps out a (composite) state machine, given a list of events:
#include <boost/sml.hpp>
namespace sml = boost::sml;
namespace aux = sml::aux;
struct e1 {};
struct e2 {};
struct e3 {};
struct e4 {};
struct e5 {};
struct sub {
auto operator()() const {
using namespace sml;
// clang-format off
return make_transition_table(
*"idle"_s + event<e3> = "sub1"_s
, "sub1"_s + event<e4> = X
);
// clang-format on
}
};
struct composite {
auto operator()() const {
using namespace sml;
// clang-format off
return make_transition_table(
*"idle"_s + event<e1> = "s1"_s
, "s1"_s + event<e2> = state<sub>
, state<sub> + event<e5> = X
);
// clang-format on
}
};
#include <boost/hana.hpp>
#include <boost/core/demangle.hpp>
#include <iostream>
#include <iomanip>
namespace mapper {
namespace hana = boost::hana;
using namespace std::string_literals;
using boost::core::demangle;
template <typename F> struct ycombine {
ycombine(F f):f(f) {}
F f;
template <typename... A>
auto operator()(A... a) const { return f(*this, a...); };
};
hana::tuple<e1,e2,e3,e4,e5> events;
template <class TSM> class Vis {
public:
explicit Vis(const TSM& sm, std::string prefix = "") : sm_{ sm }, prefix(prefix) {}
template <class TSub>
void operator()(aux::string<boost::sml::sm<TSub>>) const {
auto subname = aux::get_type_name<TSub>();
Vis nvis(sm_, prefix + '/' + subname);
sm_.template visit_current_states<aux::identity<TSub>>(nvis);
prefix = nvis.prefix;
}
template <class TState> void operator()(TState state) const {
prefix += "/"s + state.c_str();
}
private:
const TSM& sm_;
public:
mutable std::string prefix;
};
template <typename SM>
std::string get_current(SM const& sm) {
Vis<SM> v{sm};
sm.visit_current_states(v);
return v.prefix;
};
}
int main() {
using namespace mapper;
auto recurse = ycombine {
[](auto self, auto sm) {
hana::for_each(events, [=](auto ev) {
auto clone = sm;
auto from = get_current(clone);
clone.process_event(ev);
auto to = get_current(clone);
if (from != to) {
std::cout
<< std::quoted(from) << " -> "
<< std::quoted(to)
<< " [label=" << std::quoted(demangle(typeid(ev).name())) << "]\n";
self(clone);
}
});
} };
std::cout << "digraph {\n";
recurse(sml::sm<composite>{});
std::cout << "}\n";
}
Though I have no idea how useful this is, at least I could render the result with graphviz:
Second Attempt - READ THE FINE MANUAL :derp:
It looks like there was a cleaner way to do these things, especially the presence of the nested typedef transitions when reading the latest examples:
// $CXX -std=c++14 plant_uml.cpp
#include <boost/sml.hpp>
#include <cassert>
#include <iostream>
#include <string>
#include <typeinfo>
namespace sml = boost::sml;
struct e1 {};
struct e2 {};
struct e3 {};
struct e4 {};
struct guard {
bool operator()() const { return true; }
} guard;
struct action {
void operator()() {}
} action;
struct plant_uml {
auto operator()() const noexcept {
using namespace sml;
return make_transition_table(
*"idle"_s + event<e1> = "s1"_s
, "s1"_s + event<e2> [ guard ] / action = "s2"_s
, "s2"_s + event<e3> [ guard ] = "s1"_s
, "s2"_s + event<e4> / action = X
);
}
};
template <class T>
void dump_transition() noexcept {
auto src_state = std::string{sml::aux::string<typename T::src_state>{}.c_str()};
auto dst_state = std::string{sml::aux::string<typename T::dst_state>{}.c_str()};
if (dst_state == "X") {
dst_state = "[*]";
}
if (T::initial) {
std::cout << "[*] --> " << src_state << std::endl;
}
std::cout << src_state << " --> " << dst_state;
const auto has_event = !sml::aux::is_same<typename T::event, sml::anonymous>::value;
const auto has_guard = !sml::aux::is_same<typename T::guard, sml::front::always>::value;
const auto has_action = !sml::aux::is_same<typename T::action, sml::front::none>::value;
if (has_event || has_guard || has_action) {
std::cout << " :";
}
if (has_event) {
std::cout << " " << boost::sml::aux::get_type_name<typename T::event>();
}
if (has_guard) {
std::cout << " [" << boost::sml::aux::get_type_name<typename T::guard::type>() << "]";
}
if (has_action) {
std::cout << " / " << boost::sml::aux::get_type_name<typename T::action::type>();
}
std::cout << std::endl;
}
template <template <class...> class T, class... Ts>
void dump_transitions(const T<Ts...>&) noexcept {
int _[]{0, (dump_transition<Ts>(), 0)...};
(void)_;
}
template <class SM>
void dump(const SM&) noexcept {
std::cout << "#startuml" << std::endl << std::endl;
dump_transitions(typename SM::transitions{});
std::cout << std::endl << "#enduml" << std::endl;
}
int main() {
sml::sm<plant_uml> sm;
dump(sm);
}
The output, for inspiration:
#startuml
[*] --> idle
idle --> s1 : e1
s1 --> s2 : e2 [guard] / action
s2 --> s1 : e3 [guard]
s2 --> terminate : e4 / action
#enduml
Obviously, not graphviz, but actually looks more "trustworthy" due to the fact that
it resembles some kind of standard UML notation,
was written by the library author(s?)
included details that I wasn't able to (largely because of being unaware of their existence/working)
I want to efficiently parse large CSV-like files, whose order of columns I get at runtime. With Spirit Qi, I would parse each field with a lazy auxiliary parser that would select at runtime which column-specific parser to apply to each column. But X3 doesn't seem to have lazy (despite that it's listed in documentation). After reading recommendations here on SO, I've decided to write a custom parser.
It ended up being pretty nice, but now I've noticed I don't really need the pos variable be exposed anywhere outside the custom parser itself. I've tried putting it into the custom parser itself and started getting compiler errors stating that the column_value_parser object is read-only. Can I somehow put pos into the parser structure?
Simplified code that gets the compile-time error, with commented out parts of my working version:
#include <iostream>
#include <variant>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/support.hpp>
namespace helpers {
// https://bitbashing.io/std-visit.html
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
}
auto const unquoted_text_field = *(boost::spirit::x3::char_ - ',' - boost::spirit::x3::eol);
struct text { };
struct integer { };
struct real { };
struct skip { };
typedef std::variant<text, integer, real, skip> column_variant;
struct column_value_parser : boost::spirit::x3::parser<column_value_parser> {
typedef boost::spirit::unused_type attribute_type;
std::vector<column_variant>& columns;
// size_t& pos;
size_t pos;
// column_value_parser(std::vector<column_variant>& columns, size_t& pos)
column_value_parser(std::vector<column_variant>& columns)
: columns(columns)
// , pos(pos)
, pos(0)
{ }
template<typename It, typename Ctx, typename Other, typename Attr>
bool parse(It& f, It l, Ctx& ctx, Other const& other, Attr& attr) const {
auto const saved_f = f;
bool successful = false;
visit(
helpers::overloaded {
[&](skip const&) {
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::omit[unquoted_text_field]);
},
[&](text& c) {
std::string value;
successful = boost::spirit::x3::parse(f, l, unquoted_text_field, value);
if(successful) {
std::cout << "Text: " << value << '\n';
}
},
[&](integer& c) {
int value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::int_, value);
if(successful) {
std::cout << "Integer: " << value << '\n';
}
},
[&](real& c) {
double value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::double_, value);
if(successful) {
std::cout << "Real: " << value << '\n';
}
}
},
columns[pos]);
if(successful) {
pos = (pos + 1) % columns.size();
return true;
} else {
f = saved_f;
return false;
}
}
};
int main(int argc, char *argv[])
{
std::string input = "Hello,1,13.7,XXX\nWorld,2,1e3,YYY";
// Comes from external source.
std::vector<column_variant> columns = {text{}, integer{}, real{}, skip{}};
size_t pos = 0;
boost::spirit::x3::parse(
input.begin(), input.end(),
// (column_value_parser(columns, pos) % ',') % boost::spirit::x3::eol);
(column_value_parser(columns) % ',') % boost::spirit::x3::eol);
}
XY: My goal is to parse ~500 GB of pseudo-CSV files in a reasonable time on a machine with little RAM, convert into a list of (roughly) [row-number, column-name, value], then put into storage. The format is actually a little more complex than CSV: database dumps formatted in… human-friendly way, with column values being actually several small sublangauges (e.g. dates or, uh, something similar to whole apache log lines stuffed into a single field), and I'm often extracting only one specific part of each column. Different files may have different columns and in different order, which I can only learn by parsing yet another set of files containing original queries. Thankfully, Spirit makes it a breeze…
Three answers:
The easiest fix is to make pos a mutable member
The X3 hardcore answer is x3::with<>
Functional composition
1. Making pos mutable
Live On Wandbox
#include <iostream>
#include <variant>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/support.hpp>
namespace helpers {
// https://bitbashing.io/std-visit.html
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
}
auto const unquoted_text_field = *(boost::spirit::x3::char_ - ',' - boost::spirit::x3::eol);
struct text { };
struct integer { };
struct real { };
struct skip { };
typedef std::variant<text, integer, real, skip> column_variant;
struct column_value_parser : boost::spirit::x3::parser<column_value_parser> {
typedef boost::spirit::unused_type attribute_type;
std::vector<column_variant>& columns;
size_t mutable pos = 0;
struct pos_tag;
column_value_parser(std::vector<column_variant>& columns)
: columns(columns)
{ }
template<typename It, typename Ctx, typename Other, typename Attr>
bool parse(It& f, It l, Ctx& /*ctx*/, Other const& /*other*/, Attr& /*attr*/) const {
auto const saved_f = f;
bool successful = false;
visit(
helpers::overloaded {
[&](skip const&) {
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::omit[unquoted_text_field]);
},
[&](text&) {
std::string value;
successful = boost::spirit::x3::parse(f, l, unquoted_text_field, value);
if(successful) {
std::cout << "Text: " << value << '\n';
}
},
[&](integer&) {
int value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::int_, value);
if(successful) {
std::cout << "Integer: " << value << '\n';
}
},
[&](real&) {
double value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::double_, value);
if(successful) {
std::cout << "Real: " << value << '\n';
}
}
},
columns[pos]);
if(successful) {
pos = (pos + 1) % columns.size();
return true;
} else {
f = saved_f;
return false;
}
}
};
int main() {
std::string input = "Hello,1,13.7,XXX\nWorld,2,1e3,YYY";
std::vector<column_variant> columns = {text{}, integer{}, real{}, skip{}};
boost::spirit::x3::parse(
input.begin(), input.end(),
(column_value_parser(columns) % ',') % boost::spirit::x3::eol);
}
2. x3::with<>
This is similar but with better (re)entrancy and encapsulation:
Live On Wandbox
#include <iostream>
#include <variant>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/support.hpp>
namespace helpers {
// https://bitbashing.io/std-visit.html
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
}
auto const unquoted_text_field = *(boost::spirit::x3::char_ - ',' - boost::spirit::x3::eol);
struct text { };
struct integer { };
struct real { };
struct skip { };
typedef std::variant<text, integer, real, skip> column_variant;
struct column_value_parser : boost::spirit::x3::parser<column_value_parser> {
typedef boost::spirit::unused_type attribute_type;
std::vector<column_variant>& columns;
column_value_parser(std::vector<column_variant>& columns)
: columns(columns)
{ }
template<typename It, typename Ctx, typename Other, typename Attr>
bool parse(It& f, It l, Ctx const& ctx, Other const& /*other*/, Attr& /*attr*/) const {
auto const saved_f = f;
bool successful = false;
size_t& pos = boost::spirit::x3::get<pos_tag>(ctx).value;
visit(
helpers::overloaded {
[&](skip const&) {
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::omit[unquoted_text_field]);
},
[&](text&) {
std::string value;
successful = boost::spirit::x3::parse(f, l, unquoted_text_field, value);
if(successful) {
std::cout << "Text: " << value << '\n';
}
},
[&](integer&) {
int value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::int_, value);
if(successful) {
std::cout << "Integer: " << value << '\n';
}
},
[&](real&) {
double value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::double_, value);
if(successful) {
std::cout << "Real: " << value << '\n';
}
}
},
columns[pos]);
if(successful) {
pos = (pos + 1) % columns.size();
return true;
} else {
f = saved_f;
return false;
}
}
template <typename T>
struct Mutable { T mutable value; };
struct pos_tag;
auto invoke() const {
return boost::spirit::x3::with<pos_tag>(Mutable<size_t>{}) [ *this ];
}
};
int main() {
std::string input = "Hello,1,13.7,XXX\nWorld,2,1e3,YYY";
std::vector<column_variant> columns = {text{}, integer{}, real{}, skip{}};
column_value_parser p(columns);
boost::spirit::x3::parse(
input.begin(), input.end(),
(p.invoke() % ',') % boost::spirit::x3::eol);
}
3. Functional Composition
Because it's so much easier in X3, my favourite is to just generate the parser on demand.
Without requirements, this is the simplest I'd propose:
Live On Wandbox
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
namespace CSV {
struct text { };
struct integer { };
struct real { };
struct skip { };
auto const unquoted_text_field = *~x3::char_(",\n");
static inline auto as_parser(skip) { return x3::omit[unquoted_text_field]; }
static inline auto as_parser(text) { return unquoted_text_field; }
static inline auto as_parser(integer) { return x3::int_; }
static inline auto as_parser(real) { return x3::double_; }
template <typename... Spec>
static inline auto line_parser(Spec... spec) {
auto delim = ',' | &(x3::eoi | x3::eol);
return ((as_parser(spec) >> delim) >> ... >> x3::eps);
}
template <typename... Spec> static inline auto csv_parser(Spec... spec) {
return line_parser(spec...) % x3::eol;
}
}
#include <iostream>
#include <iomanip>
using namespace CSV;
int main() {
std::string const input = "Hello,1,13.7,XXX\nWorld,2,1e3,YYY";
auto f = begin(input), l = end(input);
auto p = csv_parser(text{}, integer{}, real{}, skip{});
if (parse(f, l, p)) {
std::cout << "Parsed\n";
} else {
std::cout << "Failed\n";
}
if (f!=l) {
std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n";
}
}
A version with debug information enabled:
Live On Wandbox
<line>
<try>Hello,1,13.7,XXX\nWor</try>
<CSV::text>
<try>Hello,1,13.7,XXX\nWor</try>
<success>,1,13.7,XXX\nWorld,2,</success>
</CSV::text>
<CSV::integer>
<try>1,13.7,XXX\nWorld,2,1</try>
<success>,13.7,XXX\nWorld,2,1e</success>
</CSV::integer>
<CSV::real>
<try>13.7,XXX\nWorld,2,1e3</try>
<success>,XXX\nWorld,2,1e3,YYY</success>
</CSV::real>
<CSV::skip>
<try>XXX\nWorld,2,1e3,YYY</try>
<success>\nWorld,2,1e3,YYY</success>
</CSV::skip>
<success>\nWorld,2,1e3,YYY</success>
</line>
<line>
<try>World,2,1e3,YYY</try>
<CSV::text>
<try>World,2,1e3,YYY</try>
<success>,2,1e3,YYY</success>
</CSV::text>
<CSV::integer>
<try>2,1e3,YYY</try>
<success>,1e3,YYY</success>
</CSV::integer>
<CSV::real>
<try>1e3,YYY</try>
<success>,YYY</success>
</CSV::real>
<CSV::skip>
<try>YYY</try>
<success></success>
</CSV::skip>
<success></success>
</line>
Parsed
Notes, Caveats:
With anything mutable, beware of side-effects. E.g. if you have a | b and a includes column_value_parser, the side-effect of incrementing pos will not be rolled back when a fails and b is matched instead.
In short, this makes your parse function impure.
I'm creating a logging function that on the one hand needs to be able to parse any number of parameters, but on the other hand will always be passed with __func__ and this(of the calling object).
If I use only variadic templates like this
template<typename... Args>
void
log_device_message_template(const class *object,
char const *func,
char const *const format,
Args const &... args)noexcept
{
log(format, to_str(object).c_str(),format, to_str(args).c_str()...);
}
I have to call log_device_message_template like this :
log_device_message_template(this,__func__,some format,some parameters)
so i've added the macro:
#define log_api_call(format, ...) \
log_device_message_template( \
this, __func__, "%s::%s(" format ")", ##__VA_ARGS__)
The thing is that i'm getting a seg fault, probably due to a bad formatting somewhere. adding __attribiute__(format) doesn't work due to the use of variadic template...
here is the error from the python test testing the logger:
lookup in file=***** [0]
28371: symbol=_ZN5***6logger27log_device_message_templateIINS_14*****E13****EEEvNS_21logger_component_eENS_17logger_level_eEPKcPKNS_9objectES7_DpRT_; lookup in file=**** [0]
28371: ****: error: symbol lookup error: undefined symbol: _ZN5***6logger27log_device_message_templateIINS_14****E13****EEEvNS_21logger_component_eENS_17logger_level_eEPKcPKNS_9objectES7_DpRT_ (fatal)
Here's another way to approach it which neatly avoids macros.
Note in this case I am emitting log records to stdout, but that can easily be altered. I am also using std::ostringstream to build the log message. It's not renowned for performance, so is a good candidate for a customisation point.
However, this should get you started if you like the approach:
#include <iostream>
#include <sstream>
#include <tuple>
#include <utility>
// define a constant index type
template<std::size_t N> using index_c = std::integral_constant<std::size_t, N>;
// emitting an item at index 0 has no prefix
template<class T>
void emit_item(std::ostream &os, index_c<0>, T const &t)
{
os << t;
}
// emitting an item at index N (!= 0) has a comma prefix
template<std::size_t N, class T>
void emit_item(std::ostream &os, index_c<N>, T const &t)
{
os << ", " << t;
}
// emit args 0 .. N-1
template<class Tuple, std::size_t...Is>
void emit_arglist(std::ostream &sink, Tuple &&tuple, std::index_sequence<Is...>)
{
using expand = int[];
void(expand{0,
(emit_item(sink, index_c<Is>(), std::get<Is>(tuple)), 0)...
});
};
// emitting a 'more' at index 0 has a prefix
template<class T>
void emit_more(std::ostream &os, index_c<0>, T const &t)
{
os << " : " << t;
}
// emitting a 'more' at index N (!= 0) has a space prefix
template<std::size_t N, class T>
void emit_more(std::ostream &os, index_c<N>, T const &t)
{
os << " " << t;
}
template<class Tuple, std::size_t...Is>
void emit_more(std::ostream &sink, Tuple &&tuple, std::index_sequence<Is...>)
{
using expand = int[];
void(expand{0,
(emit_more(sink, index_c<Is>(), std::get<Is>(tuple)), 0)...
});
};
template<typename... Args, typename ...MoreStuff>
std::string
make_log_string(const char *object,
char const *func,
std::tuple<Args const &...> const& args,
MoreStuff&&...morestuff) noexcept
{
std::ostringstream ss;
ss << object << "::" << func << '(';
emit_arglist(ss, args, std::make_index_sequence<sizeof...(Args)>());
ss << ')';
emit_more(ss, std::tie(morestuff...), std::make_index_sequence<sizeof...(MoreStuff)>());
return ss.str();
}
// syntactic sugar for indicating arguments
template<class...Arg>
decltype(auto) args(Arg const&...args)
{
return std::tie(args...);
}
int main()
{
int a = 0, b = 1, c = 2;
std::string sa = "xxx", sb = "yyy", sc = "zzz";
const char* Class = "foo";
const char* Func = "var";
std::cout << make_log_string(Class, Func, args(a, b, c)) << std::endl;
std::cout << make_log_string(Class, Func, args(sa, b, sc)) << std::endl;
std::cout << make_log_string(Class, Func, args(sa, b, sc), "more stuff") << std::endl;
std::cout << make_log_string(Class, Func, args(), "empty", "argument", "list") << std::endl;
}
expected output:
foo::var(0, 1, 2)
foo::var(xxx, 1, zzz)
foo::var(xxx, 1, zzz) : more stuff
foo::var() : empty argument list
And with a bit more boilerplate we can write this:
std::cout << make_log_string(method(Class, Func)(a, b, c)) << std::endl;
std::cout << make_log_string(method(Class, Func)(sa, b, sc)) << std::endl;
std::cout << make_log_string(method(Class, Func)(sa, b, sc), "more stuff") << std::endl;
std::cout << make_log_string(method(Class, Func)(), "empty", "argument", "list") << std::endl;
Here it is:
#include <iostream>
#include <sstream>
#include <tuple>
#include <utility>
template<std::size_t N> using index_c = std::integral_constant<std::size_t, N>;
template<class T>
void emit_item(std::ostream &os, index_c<0>, T const &t)
{
os << t;
}
template<std::size_t N, class T>
void emit_item(std::ostream &os, index_c<N>, T const &t)
{
os << ", " << t;
}
template<class Tuple, std::size_t...Is>
void emit_arglist(std::ostream &sink, Tuple &&tuple, std::index_sequence<Is...>)
{
using expand = int[];
void(expand{0,
(emit_item(sink, index_c<Is>(), std::get<Is>(tuple)), 0)...
});
};
template<class T>
void emit_more(std::ostream &os, index_c<0>, T const &t)
{
os << " : " << t;
}
template<std::size_t N, class T>
void emit_more(std::ostream &os, index_c<N>, T const &t)
{
os << " " << t;
}
template<class Tuple, std::size_t...Is>
void emit_more(std::ostream &sink, Tuple &&tuple, std::index_sequence<Is...>)
{
using expand = int[];
void(expand{0,
(emit_more(sink, index_c<Is>(), std::get<Is>(tuple)), 0)...
});
};
template<class...Args>
struct method_with_args;
struct method
{
constexpr method(const char* c, const char* f) : klass(c), func(f) {}
const char* klass;
const char* func;
template<class...Args>
auto operator()(Args const&...args) -> method_with_args<Args...>;
friend std::ostream& operator<<(std::ostream& os, const method& m)
{
return os << m.klass << "::" << m.func;
}
};
template<class...Args>
struct method_with_args
{
friend std::ostream& operator<<(std::ostream& os, method_with_args const& ma)
{
os << ma.m << '(';
emit_arglist(os, ma.args, std::make_index_sequence<sizeof...(Args)>());
return os << ')';
}
method m;
std::tuple<Args const&...> args;
};
template<class...Args>
auto method::operator()(Args const&...args) -> method_with_args<Args...>
{
return method_with_args<Args...>{*this, std::tie(args...)};
}
struct function
{
const char* name;
};
template<typename Method, typename ...MoreStuff>
std::string
make_log_string(Method m,
MoreStuff &&...morestuff) noexcept
{
std::ostringstream ss;
ss << m;
emit_more(ss, std::tie(morestuff...), std::make_index_sequence<sizeof...(MoreStuff)>());
return ss.str();
}
int main()
{
int a = 0, b = 1, c = 2;
std::string sa = "xxx", sb = "yyy", sc = "zzz";
const char *Class = "foo";
const char *Func = "var";
std::cout << make_log_string(method(Class, Func)(a, b, c)) << std::endl;
std::cout << make_log_string(method(Class, Func)(sa, b, sc)) << std::endl;
std::cout << make_log_string(method(Class, Func)(sa, b, sc), "more stuff") << std::endl;
std::cout << make_log_string(method(Class, Func)(), "empty", "argument", "list") << std::endl;
}
I would like to split a string into parts:
input = "part1/part2/part3/also3"
and fill the structure that consist of three std::string with these parts.
struct strings
{
std::string a; // <- part1
std::string b; // <- part2
std::string c; // <- part3/also3
};
However my parser seems to merge the parts together and store it into the first std::string.
Here is the code on coliru
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapted.hpp>
namespace qi = ::boost::spirit::qi;
struct strings
{
std::string a;
std::string b;
std::string c;
};
BOOST_FUSION_ADAPT_STRUCT(strings,
(std::string, a) (std::string, b) (std::string, c))
template <typename It>
struct split_string_grammar: qi::grammar<It, strings ()>
{
split_string_grammar (int parts)
: split_string_grammar::base_type (split_string)
{
assert (parts > 0);
using namespace qi;
split_string = repeat (parts-1) [part > '/'] > last_part;
part = +(~char_ ("/"));
last_part = +char_;
BOOST_SPIRIT_DEBUG_NODES ((split_string) (part) (last_part))
}
private:
qi::rule<It, strings ()> split_string;
qi::rule<It, std::string ()> part, last_part;
};
int main ()
{
std::string const input { "one/two/three/four" };
auto const last = input.end ();
auto first = input.begin ();
// split into 3 parts.
split_string_grammar<decltype (first)> split_string (3);
strings ss;
bool ok = qi::parse (first, last, split_string, ss);
std::cout << "Parsed: " << ok << "\n";
if (ok) {
std::cout << "a:" << ss.a << "\n";
std::cout << "b:" << ss.b << "\n";
std::cout << "c:" << ss.c << "\n";
}
}
The output is:
Parsed: 1
a:onetwo
b:three/four
c:
while I expected:
Parsed: 1
a:one
b:two
c:three/four
I'd like not to modify the grammar heavily and leave "repeat" statement in it, because the "real" grammar is much more complex of course and I will need to have it there. Just need to find the way to disable the concatenations. I tried
repeat (parts-1) [as_string[part] > '/']
but that does not compile.
The trouble here is specifically that qi::repeat is documented to expose a container of element-types.
Now, because the exposed attribute type of the rule (strings) is not a container-type, Spirit "knows" how to flatten the values.
Of course it's not what you wanted in this case, but usually this heuristic makes for really convenient accumulation of string values.
Fix 1: use a container attribute
You could witness the reverse fix by getting rid of the non-container (sequence) target attribute:
Live On Coliru
//#define BOOST_SPIRIT_DEBUG
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapted.hpp>
namespace qi = ::boost::spirit::qi;
using strings = std::vector<std::string>;
template <typename It>
struct split_string_grammar: qi::grammar<It, strings ()>
{
split_string_grammar (int parts)
: split_string_grammar::base_type (split_string)
{
assert (parts > 0);
using namespace qi;
split_string = repeat (parts-1) [part > '/']
> last_part
;
part = +(~char_ ("/"))
;
last_part = +char_
;
BOOST_SPIRIT_DEBUG_NODES ((split_string) (part) (last_part))
}
private:
qi::rule<It, strings ()> split_string;
qi::rule<It, std::string ()> part, last_part;
};
int main ()
{
std::string const input { "one/two/three/four" };
auto const last = input.end ();
auto first = input.begin ();
// split into 3 parts.
split_string_grammar<decltype (first)> split_string (3);
strings ss;
bool ok = qi::parse (first, last, split_string, ss);
std::cout << "Parsed: " << ok << "\n";
if (ok) {
for(auto i = 0ul; i<ss.size(); ++i)
std::cout << static_cast<char>('a'+i) << ":" << ss[i] << "\n";
}
}
What you really wanted:
Of course you want to keep the struct/sequence adaptation (?); In this case that's really tricky because as soon as you use any kind of Kleene operator (*,%) or qi::repeat you'll have the attribute transformation rules as outlined above, ruining your mood.
Luckily, I just remembered I have a hacky solution based on the auto_ parser. Note the caveat in this older answer though:
Read empty values with boost::spirit
CAVEAT Specializing for std::string directly like this might not be the best idea (it might not always be appropriate and might interact badly with other parsers).
By default create_parser<std::string> is not defined, so you might decide this usage is good enough for your case:
Live On Coliru
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
struct strings {
std::string a;
std::string b;
std::string c;
};
namespace boost { namespace spirit { namespace traits {
template <> struct create_parser<std::string> {
typedef proto::result_of::deep_copy<
BOOST_TYPEOF(
qi::lexeme [+(qi::char_ - '/')] | qi::attr("(unspecified)")
)
>::type type;
static type call() {
return proto::deep_copy(
qi::lexeme [+(qi::char_ - '/')] | qi::attr("(unspecified)")
);
}
};
}}}
BOOST_FUSION_ADAPT_STRUCT(strings, (std::string, a)(std::string, b)(std::string, c))
template <typename Iterator>
struct google_parser : qi::grammar<Iterator, strings()> {
google_parser() : google_parser::base_type(entry, "contacts") {
using namespace qi;
entry =
skip('/') [auto_]
;
}
private:
qi::rule<Iterator, strings()> entry;
};
int main() {
using It = std::string::const_iterator;
google_parser<It> p;
std::string const input = "part1/part2/part3/also3";
It f = input.begin(), l = input.end();
strings ss;
bool ok = qi::parse(f, l, p >> *qi::char_, ss, ss.c);
if (ok)
{
std::cout << "a:" << ss.a << "\n";
std::cout << "b:" << ss.b << "\n";
std::cout << "c:" << ss.c << "\n";
}
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
Prints
a:part1
b:part2
c:part3/also3
Update/Bonus
In reponse to the OP's own answer I wanted to challenge myself to write it more generically indeed.
The main thing is to to write set_field_ in such a way that it doesn't know/assume more than required about the destination sequence type.
With a bit of Boost Fusion magic that became:
struct set_field_
{
template <typename Seq, typename Value>
void operator() (Seq& seq, Value const& src, unsigned idx) const {
fus::fold(seq, 0u, Visit<Value> { idx, src });
}
private:
template <typename Value>
struct Visit {
unsigned target_idx;
Value const& value;
template <typename B>
unsigned operator()(unsigned i, B& dest) const {
if (target_idx == i) {
boost::spirit::traits::assign_to(value, dest);
}
return i + 1;
}
};
};
It has the added flexibility of applying Spirit's attribute compatibility rules¹. So, you can use the same grammar with both the following types:
struct strings {
std::string a, b, c;
};
struct alternative {
std::vector<char> first;
std::string second;
std::string third;
};
To drive the point home, I made the adaptation of the second struct reverse the field order:
BOOST_FUSION_ADAPT_STRUCT(strings, a, b, c)
BOOST_FUSION_ADAPT_STRUCT(alternative, third, second, first) // REVERSE ORDER :)
Without further ado, the demo program:
Live On Coliru
#define BOOST_SPIRIT_USE_PHOENIX_V3
#define BOOST_RESULT_OF_USE_DECLTYPE
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/algorithm/iteration.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace fus = boost::fusion;
struct strings {
std::string a, b, c;
};
struct alternative {
std::vector<char> first;
std::string second;
std::string third;
};
BOOST_FUSION_ADAPT_STRUCT(strings, a, b, c)
BOOST_FUSION_ADAPT_STRUCT(alternative, third, second, first) // REVERSE ORDER :)
// output helpers for demo:
namespace {
inline std::ostream& operator<<(std::ostream& os, strings const& data) {
return os
<< "a:\"" << data.a << "\" "
<< "b:\"" << data.b << "\" "
<< "c:\"" << data.c << "\" ";
}
inline std::ostream& operator<<(std::ostream& os, alternative const& data) {
os << "first: vector<char> { \""; os.write(&data.first[0], data.first.size()); os << "\" } ";
os << "second: \"" << data.second << "\" ";
os << "third: \"" << data.third << "\" ";
return os;
}
}
struct set_field_
{
template <typename Seq, typename Value>
void operator() (Seq& seq, Value const& src, unsigned idx) const {
fus::fold(seq, 0u, Visit<Value> { idx, src });
}
private:
template <typename Value>
struct Visit {
unsigned target_idx;
Value const& value;
template <typename B>
unsigned operator()(unsigned i, B& dest) const {
if (target_idx == i) {
boost::spirit::traits::assign_to(value, dest);
}
return i + 1;
}
};
};
boost::phoenix::function<set_field_> const set_field = {};
template <typename It, typename Target>
struct split_string_grammar: qi::grammar<It, Target(), qi::locals<unsigned> >
{
split_string_grammar (int parts)
: split_string_grammar::base_type (split_string)
{
assert (parts > 0);
using namespace qi;
using boost::phoenix::val;
_a_type _current; // custom placeholder
split_string =
eps [ _current = 0u ]
> repeat (parts-1)
[part [ set_field(_val, _1, _current++) ] > '/']
> last_part [ set_field(_val, _1, _current++) ];
part = +(~char_ ("/"));
last_part = +char_;
BOOST_SPIRIT_DEBUG_NODES ((split_string) (part) (last_part))
}
private:
qi::rule<It, Target(), qi::locals<unsigned> > split_string;
qi::rule<It, std::string()> part, last_part;
};
template <size_t N = 3, typename Target>
void run_test(Target target) {
using It = std::string::const_iterator;
std::string const input { "one/two/three/four" };
It first = input.begin(), last = input.end();
split_string_grammar<It, Target> split_string(N);
bool ok = qi::parse (first, last, split_string, target);
if (ok) {
std::cout << target << '\n';
} else {
std::cout << "Parse failed\n";
}
if (first != last)
std::cout << "Remaining input left unparsed: '" << std::string(first, last) << "'\n";
}
int main ()
{
run_test(strings {});
run_test(alternative {});
}
Output:
a:"one" b:"two" c:"three/four"
first: vector<char> { "three/four" } second: "two" third: "one"
¹ as with BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
Besides sehe's suggestions one more possible way is to use semantic actions (coliru):
struct set_field_
{
void operator() (strings& dst, std::string const& src, unsigned& idx) const
{
assert (idx < 3);
switch (idx++) {
case 0: dst.a = src; break;
case 1: dst.b = src; break;
case 2: dst.c = src; break;
}
}
};
boost::phoenix::function<set_field_> const set_field { set_field_ {} };
template <typename It>
struct split_string_grammar: qi::grammar<It, strings (), qi::locals<unsigned> >
{
split_string_grammar (int parts)
: split_string_grammar::base_type (split_string)
{
assert (parts > 0);
using namespace qi;
using boost::phoenix::val;
split_string = eps [ _a = val (0) ]
> repeat (parts-1) [part [ set_field (_val, _1, _a) ] > '/']
> last_part [ set_field (_val, _1, _a) ];
part = +(~char_ ("/"));
last_part = +char_;
BOOST_SPIRIT_DEBUG_NODES ((split_string) (part) (last_part))
}
private:
qi::rule<It, strings (), qi::locals<unsigned> > split_string;
qi::rule<It, std::string ()> part, last_part;
};
How can I create one comparator to compare on different fields. Different fields can have different types (uint or string). Should I use T *?
It is necessary to reduce the code length.
template<typename T>
class ComparatorSelector
{
public:
struct CompareByLabel{
bool operator() ( const T & iRight, const T & iLeft )
{
return iRight->m_label > iLeft->m_label;
}
};
struct CompareByHouseNumber{
bool operator() ( const T & iRight, const T & iLeft )
{
return iRight->m_houseNumber > iLeft->m_houseNumber;
}
};
//...
};
template< class T, class C, typename W >
class SearchIndex
{
public:
SearchIndex() {}
void Build( std::vector< T > iElems, C iComparator, std::ofstream oStream )
{
std::map< T *, size_t> numbersOfElems;
for( class std::vector<T>::iterator it = iElems.begin(); it != iElems.end(); ++it){
m_elems.insert( &(*it));
numbersOfElems[&(*it)] = m_elems.end - it ;
}
oStream << m_elems.size();
for( class std::multiset< T * >::iterator it = m_elems.begin(); it!= m_elems.end(); ++it )
oStream << numbersOfElems[*it];
m_compareMode = iComparator;
}
//....
}
You can use pointers to members to customize your comparator objects. The slower but simpler approach is this:
#include <iostream>
template <typename Type, typename Class>
class comparator
{
Type Class::*d_member;
public:
comparator(Type Class::*member): d_member(member) {}
bool operator()(Class const& object0, Class const& object1) const {
return object0.*(this->d_member) < object1.*(this->d_member);
}
};
template <typename Type, typename Class>
comparator<Type, Class>
make_comparator(Type Class::*member)
{
return comparator<Type, Class>(member);
}
int main()
{
typedef std::pair<int, double> pair;
pair p0(17, 3.14);
pair p1(42, 2.7);
std::cout << std::boolalpha
<< "first: " << make_comparator(&pair::first)(p0, p1) << ' '
<< "second: " << make_comparator(&pair::second)(p0, p1) << ' '
<< '\n';
}
Since this version uses a pointer to member at run-time, it cannot be easily inlined and, thus, isn't as fast as you'd possibly want it to be. The member can also be embedded into the comparator's type making both its use a bit annoying:
template <typename Type, typename Class, Type Class::*Member>
class comparator
{
public:
bool operator()(Class const& object0, Class const& object1) const {
return object0.*Member < object1.*Member;
}
};
int main()
{
typedef std::pair<int, double> pair;
pair p0(17, 3.14);
pair p1(42, 2.7);
std::cout << std::boolalpha
<< "first: " << comparator<int, pair, &pair::first>()(p0, p1) << ' '
<< "second: " << comparator<double, pair, &pair::second>()(p0, p1) << ' '
<< '\n';
}
This is an example of a comparator that uses different fields of different types:
#include <set>
using namespace std;
class House {
public:
string m_label;
int m_houseNumber;
};
class HouseCompare {
public:
bool operator()( const House& a, const House& b)
{
if (a.m_houseNumber>0 && b.m_houseNumber>0)
return a.m_houseNumber < b.m_houseNumber;
else if (a.m_houseNumber>0)
return false;
else if (b.m_houseNumber)
return true;
else
return a.m_label < b.m_label;
}
};
int main(int argc, char *argv[])
{
typedef multiset<House, HouseCompare> Houses;
Houses houses;
House house_data[] = {
{"foo", 1},
{"foo1", 0},
{"foo0", 0},
{"foo", 2}
};
houses.insert (house_data, house_data+sizeof(house_data)/sizeof(House));
for (Houses::iterator i = houses.begin (); i != houses.end (); ++i)
cout << i->m_houseNumber << ": " << i->m_label << endl;
return 0;
}
Output:
0: foo0
0: foo1
1: foo
2: foo