Commit 086d5127 authored by Dominik Charousset's avatar Dominik Charousset

Improve documentation of `message`

parent 3935def8
...@@ -42,24 +42,13 @@ namespace caf { ...@@ -42,24 +42,13 @@ namespace caf {
class message_handler; class message_handler;
/** /**
* Describes a fixed-length copy-on-write tuple with elements of any type. * Describes a fixed-length, copy-on-write, type-erased
* tuple with elements of any type.
*/ */
class message { class message {
public: public:
/**
* A raw pointer to the data.
*/
using raw_ptr = detail::message_data*;
/**
* A (COW) smart pointer to the data.
*/
using data_ptr = detail::message_data::ptr;
/** /**
* Creates an empty tuple. * Creates an empty message.
*/ */
message() = default; message() = default;
...@@ -84,7 +73,7 @@ class message { ...@@ -84,7 +73,7 @@ class message {
message& operator=(const message&) = default; message& operator=(const message&) = default;
/** /**
* Gets the size of this tuple. * Returns the size of this message.
*/ */
inline size_t size() const { inline size_t size() const {
return m_vals ? m_vals->size() : 0; return m_vals ? m_vals->size() : 0;
...@@ -115,21 +104,24 @@ class message { ...@@ -115,21 +104,24 @@ class message {
} }
/** /**
* Creates a new message of size `n` starting at element `pos`. * Creates a new message of size `n` starting at the element at position `p`.
*/ */
message slice(size_t pos, size_t n) const; message slice(size_t p, size_t n) const;
/** /**
* Gets a mutable pointer to the element at position @p p. * Returns a mutable pointer to the element at position `p`.
*/ */
void* mutable_at(size_t p); void* mutable_at(size_t p);
/** /**
* Gets a const pointer to the element at position @p p. * Returns a const pointer to the element at position `p`.
*/ */
const void* at(size_t p) const; const void* at(size_t p) const;
const char* uniform_name_at(size_t pos) const; /**
* Returns the uniform type name for the element at position `p`.
*/
const char* uniform_name_at(size_t p) const;
/** /**
* Returns @c true if `*this == other, otherwise false. * Returns @c true if `*this == other, otherwise false.
...@@ -144,137 +136,155 @@ class message { ...@@ -144,137 +136,155 @@ class message {
} }
/** /**
* Returns the value at @p as instance of @p T. * Returns the value at position `p` as const reference of type `T`.
*/ */
template <class T> template <class T>
inline const T& get_as(size_t p) const { const T& get_as(size_t p) const {
CAF_REQUIRE(match_element(p, detail::type_nr<T>::value, &typeid(T))); CAF_REQUIRE(match_element(p, detail::type_nr<T>::value, &typeid(T)));
return *reinterpret_cast<const T*>(at(p)); return *reinterpret_cast<const T*>(at(p));
} }
/** /**
* Returns the value at @p as mutable data_ptr& of type @p T&. * Returns the value at position `p` as mutable reference of type `T`.
*/ */
template <class T> template <class T>
inline T& get_as_mutable(size_t p) { T& get_as_mutable(size_t p) {
CAF_REQUIRE(match_element(p, detail::type_nr<T>::value, &typeid(T))); CAF_REQUIRE(match_element(p, detail::type_nr<T>::value, &typeid(T)));
return *reinterpret_cast<T*>(mutable_at(p)); return *reinterpret_cast<T*>(mutable_at(p));
} }
/** /**
* Returns a copy-on-write pointer to the internal data. * Returns `handler(*this)`.
*/ */
inline data_ptr& vals() { optional<message> apply(message_handler handler);
return m_vals;
}
/** /**
* Returns a const copy-on-write pointer to the internal data. * Filters this message by applying slices of it to `handler` and returns
* the remaining elements of this operation. Slices are generated in the
* sequence `[0, size)`, `[0, size-1)`, `...` , `[1, size-1)`, `...`,
* `[size-1, size)`. Whenever a slice matches, it is removed from the message
* and the next slice starts at the *same* index on the reduced message.
*
* For example:
*
* ~~~
* auto msg = make_message(1, 2.f, 3.f, 4);
* // filter float and integer pairs
* auto msg2 = msg.filter({
* [](float, float) { },
* [](int, int) { }
* });
* assert(msg2 == make_message(1, 4));
* ~~~
*
* Step-by-step explanation:
* - Slice 1: `(1, 2.f, 3.f, 4)`, no match
* - Slice 2: `(1, 2.f, 3.f)`, no match
* - Slice 3: `(1, 2.f)`, no match
* - Slice 4: `(1)`, no match
* - Slice 5: `(2.f, 3.f, 4)`, no match
* - Slice 6: `(2.f, 3.f)`, *match*; new message is `(1, 4)`
* - Slice 7: `(4)`, no match
*
* Slice 7 is `(4)`, i.e., does not contain the first element, because the
* match on slice 6 occurred at index position 1. The function `filter`
* iterates a message only once, from left to right.
*/ */
inline const data_ptr& vals() const { message filter(message_handler handler) const;
return m_vals;
}
/** /**
* Returns a const copy-on-write pointer to the internal data. * Stores the name of a command line option ("<long name>[,<short name>]")
* along with a description and a callback.
*/ */
inline const data_ptr& cvals() const { struct cli_arg {
return m_vals;
}
/** /**
* Checks whether this message is dynamically typed, i.e., * Full name of this CLI argument using format "<long name>[,<short name>]"
* its types were not known at compile time.
*/ */
bool dynamically_typed() const; std::string name;
/** /**
* Applies `handler` to this message and returns the result * Desciption of this CLI argument for the auto-generated help text.
* of `handler(*this)`.
*/ */
optional<message> apply(message_handler handler); std::string text;
/** /**
* Returns a new message consisting of all elements that were *not* * Returns `true` on a match, `false` otherwise.
* matched by `handler`.
*/ */
message filter(message_handler handler) const; std::function<bool (const std::string&)> fun;
/** /**
* Stores the name of a command line option ("<long name>[,<short name>]") * Creates a CLI argument without data.
* along with a description and a callback. */
cli_arg(std::string name, std::string text);
/**
* Creates a CLI argument storing its matched argument in `dest`.
*/
cli_arg(std::string name, std::string text, std::string& dest);
/**
* Creates a CLI argument appending matched arguments to `dest`.
*/
cli_arg(std::string name, std::string text, std::vector<std::string>& dest);
/**
* Creates a CLI argument for converting from strings,
* storing its matched argument in `dest`.
*/ */
struct cli_arg {
std::string name;
std::string text;
std::function<bool (const std::string&)> fun;
inline cli_arg(std::string nstr, std::string tstr)
: name(std::move(nstr)),
text(std::move(tstr)) {
// nop
}
inline cli_arg(std::string nstr, std::string tstr, std::string& arg)
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool{
arg = str;
return true;
};
}
inline cli_arg(std::string nstr, std::string tstr, std::vector<std::string>& arg)
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool {
arg.push_back(str);
return true;
};
}
template <class T> template <class T>
cli_arg(std::string nstr, std::string tstr, T& arg) cli_arg(std::string name, std::string text, T& dest);
: name(std::move(nstr)),
text(std::move(tstr)) { /**
fun = [&arg](const std::string& str) -> bool { * Creates a CLI argument for converting from strings,
auto res = from_string<T>(str); * appending matched arguments to `dest`.
if (!res) { */
return false;
}
arg = *res;
return true;
};
}
template <class T> template <class T>
cli_arg(std::string nstr, std::string tstr, std::vector<T>& arg) cli_arg(std::string name, std::string text, std::vector<T>& dest);
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool {
auto res = from_string<T>(str);
if (!res) {
return false;
}
arg.push_back(*res);
return true;
};
}
}; };
struct cli_res; struct cli_res;
/** /**
* A simplistic interface for using `filter` to parse command line options. * A simplistic interface for using `filter` to parse command line options.
* Usage example:
*
* ~~~
* int main(int argc, char** argv) {
* uint16_t port;
* string host = "localhost";
* auto res = message_builder(argv + 1, argv + argc).filter_cli({
* {"port,p", "set port", port},
* {"host,H", "set host (default: localhost)", host},
* {"verbose,v", "enable verbose mode"}
* });
* if (res.opts.count("help") > 0) {
* // CLI arguments contained "-h", "--help", or "-?" (builtin);
* // note: the help text has already been printed to stdout
* return 0;
* }
* if (!res.remainder.empty()) {
* // ... filter did not consume all CLI arguments ...
* }
* if (res.opts.count("verbose") > 0) {
* // ... enable verbose mode ...
* }
* // ...
* }
* ~~~
*/ */
cli_res filter_cli(std::vector<cli_arg> args) const; cli_res filter_cli(std::vector<cli_arg> args) const;
/** /**
* Queries whether the element at `pos` is of type `T`. * Queries whether the element at position `p` is of type `T`.
* @param pos Index of element in question.
*/ */
template <class T> template <class T>
bool match_element(size_t pos) const { bool match_element(size_t p) const {
const std::type_info* rtti = nullptr; const std::type_info* rtti = nullptr;
if (detail::type_nr<T>::value == 0) { if (detail::type_nr<T>::value == 0) {
rtti = &typeid(T); rtti = &typeid(T);
} }
return match_element(pos, detail::type_nr<T>::value, rtti); return match_element(p, detail::type_nr<T>::value, rtti);
} }
/** /**
...@@ -287,9 +297,30 @@ class message { ...@@ -287,9 +297,30 @@ class message {
return size() == sizeof...(Ts) && match_elements_impl(p0, tlist); return size() == sizeof...(Ts) && match_elements_impl(p0, tlist);
} }
/** @cond PRIVATE */ /** @cond PRIVATE */
using raw_ptr = detail::message_data*;
using data_ptr = detail::message_data::ptr;
explicit message(raw_ptr);
explicit message(const data_ptr& vals);
void reset();
inline data_ptr& vals() {
return m_vals;
}
inline const data_ptr& vals() const {
return m_vals;
}
inline const data_ptr& cvals() const {
return m_vals;
}
inline uint32_t type_token() const { inline uint32_t type_token() const {
return m_vals ? m_vals->type_token() : 0xFFFFFFFF; return m_vals ? m_vals->type_token() : 0xFFFFFFFF;
} }
...@@ -298,16 +329,10 @@ class message { ...@@ -298,16 +329,10 @@ class message {
m_vals.detach(); m_vals.detach();
} }
void reset();
explicit message(raw_ptr);
inline std::string tuple_type_names() const { inline std::string tuple_type_names() const {
return m_vals->tuple_type_names(); return m_vals->tuple_type_names();
} }
explicit message(const data_ptr& vals);
struct move_from_tuple_helper { struct move_from_tuple_helper {
template <class... Ts> template <class... Ts>
inline message operator()(Ts&... vs) { inline message operator()(Ts&... vs) {
...@@ -321,14 +346,7 @@ class message { ...@@ -321,14 +346,7 @@ class message {
return detail::apply_args(f, detail::get_indices(tup), tup); return detail::apply_args(f, detail::get_indices(tup), tup);
} }
/** bool match_element(size_t p, uint16_t tnr, const std::type_info* rtti) const;
* Tries to match element at position `pos` to given RTTI.
* @param pos Index of element in question.
* @param typenr Number of queried type or `0` for custom types.
* @param rtti Queried type or `nullptr` for builtin types.
*/
bool match_element(size_t pos, uint16_t typenr,
const std::type_info* rtti) const;
template <class T, class... Ts> template <class T, class... Ts>
bool match_elements(detail::type_list<T, Ts...> list) const { bool match_elements(detail::type_list<T, Ts...> list) const {
...@@ -344,15 +362,17 @@ class message { ...@@ -344,15 +362,17 @@ class message {
detail::type_list<>) const { detail::type_list<>) const {
return true; // end of recursion return true; // end of recursion
} }
template <size_t P, class T, class... Ts> template <size_t P, class T, class... Ts>
bool match_elements_impl(std::integral_constant<size_t, P>, bool match_elements_impl(std::integral_constant<size_t, P>,
detail::type_list<T, Ts...>) const { detail::type_list<T, Ts...>) const {
std::integral_constant<size_t, P + 1> next_p; std::integral_constant<size_t, P + 1> next_p;
detail::type_list<Ts...> next_list; detail::type_list<Ts...> next_list;
return match_element<T>(P) return match_element<T>(P) && match_elements_impl(next_p, next_list);
&& match_elements_impl(next_p, next_list);
} }
message filter_impl(size_t start, message_handler handler) const; message filter_impl(size_t start, message_handler handler) const;
data_ptr m_vals; data_ptr m_vals;
}; };
...@@ -364,10 +384,12 @@ struct message::cli_res { ...@@ -364,10 +384,12 @@ struct message::cli_res {
* Stores the remaining (unmatched) arguments. * Stores the remaining (unmatched) arguments.
*/ */
message remainder; message remainder;
/** /**
* Stores the names of all active options. * Stores the names of all active options.
*/ */
std::set<std::string> opts; std::set<std::string> opts;
/** /**
* Stores the automatically generated help text. * Stores the automatically generated help text.
*/ */
...@@ -388,6 +410,10 @@ inline bool operator!=(const message& lhs, const message& rhs) { ...@@ -388,6 +410,10 @@ inline bool operator!=(const message& lhs, const message& rhs) {
return !(lhs == rhs); return !(lhs == rhs);
} }
/**
* Unboxes atom constants, i.e., converts `atom_constant<V>` to `V`.
* @relates message
*/
template <class T> template <class T>
struct unbox_message_element { struct unbox_message_element {
using type = T; using type = T;
...@@ -399,24 +425,24 @@ struct unbox_message_element<atom_constant<V>> { ...@@ -399,24 +425,24 @@ struct unbox_message_element<atom_constant<V>> {
}; };
/** /**
* Creates a new `message` containing the elements `args...`. * Returns a new `message` containing the values `(v, vs...)`.
* @relates message * @relates message
*/ */
template <class T, class... Ts> template <class V, class... Vs>
typename std::enable_if< typename std::enable_if<
!std::is_same<message, typename std::decay<T>::type>::value !std::is_same<message, typename std::decay<V>::type>::value
|| (sizeof...(Ts) > 0), || (sizeof...(Vs) > 0),
message message
>::type >::type
make_message(T&& arg, Ts&&... args) { make_message(V&& v, Vs&&... vs) {
using storage using storage
= detail::tuple_vals<typename unbox_message_element< = detail::tuple_vals<typename unbox_message_element<
typename detail::strip_and_convert<T>::type typename detail::strip_and_convert<V>::type
>::type, >::type,
typename unbox_message_element< typename unbox_message_element<
typename detail::strip_and_convert<Ts>::type typename detail::strip_and_convert<Vs>::type
>::type...>; >::type...>;
auto ptr = new storage(std::forward<T>(arg), std::forward<Ts>(args)...); auto ptr = new storage(std::forward<V>(v), std::forward<Vs>(vs)...);
return message{detail::message_data::ptr{ptr}}; return message{detail::message_data::ptr{ptr}};
} }
...@@ -428,6 +454,38 @@ inline message make_message(message other) { ...@@ -428,6 +454,38 @@ inline message make_message(message other) {
return std::move(other); return std::move(other);
} }
/******************************************************************************
* template member function implementations *
******************************************************************************/
template <class T>
message::cli_arg::cli_arg(std::string nstr, std::string tstr, T& arg)
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool {
auto res = from_string<T>(str);
if (!res) {
return false;
}
arg = *res;
return true;
};
}
template <class T>
message::cli_arg::cli_arg(std::string nstr, std::string tstr, std::vector<T>& arg)
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool {
auto res = from_string<T>(str);
if (!res) {
return false;
}
arg.push_back(*res);
return true;
};
}
} // namespace caf } // namespace caf
#endif // CAF_MESSAGE_HPP #endif // CAF_MESSAGE_HPP
...@@ -264,4 +264,28 @@ message::cli_res message::filter_cli(std::vector<cli_arg> cliargs) const { ...@@ -264,4 +264,28 @@ message::cli_res message::filter_cli(std::vector<cli_arg> cliargs) const {
return {res, std::move(opts), std::move(helptext)}; return {res, std::move(opts), std::move(helptext)};
} }
message::cli_arg::cli_arg(std::string nstr, std::string tstr)
: name(std::move(nstr)),
text(std::move(tstr)) {
// nop
}
message::cli_arg::cli_arg(std::string nstr, std::string tstr, std::string& arg)
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool {
arg = str;
return true;
};
}
message::cli_arg::cli_arg(std::string nstr, std::string tstr,
std::vector<std::string>& arg)
: name(std::move(nstr)), text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool {
arg.push_back(str);
return true;
};
}
} // namespace caf } // namespace caf
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment