Commit 21b8e8c5 authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'topic/string-view'

parents 99b35153 e3060212
......@@ -104,6 +104,7 @@ set(LIBCAF_CORE_SRCS
src/stream_aborter.cpp
src/stream_manager.cpp
src/stream_priority.cpp
src/string_view.cpp
src/stringification_inspector.cpp
src/sync_request_bouncer.cpp
src/term.cpp
......
......@@ -30,6 +30,7 @@
#include "caf/config_option.hpp"
#include "caf/config_option_set.hpp"
#include "caf/config_value.hpp"
#include "caf/dictionary.hpp"
#include "caf/fwd.hpp"
#include "caf/is_typed_actor.hpp"
#include "caf/named_actor_config.hpp"
......@@ -79,7 +80,7 @@ public:
using group_module_factory_vector = std::vector<group_module_factory>;
using config_map = std::map<std::string, config_value::dictionary>;
using config_map = dictionary<config_value::dictionary>;
using string_list = std::vector<std::string>;
......@@ -101,11 +102,11 @@ public:
// -- properties -------------------------------------------------------------
/// @private
std::map<std::string, config_value::dictionary> content;
dictionary<config_value::dictionary> content;
/// Sets a config by using its INI name `config_name` to `config_value`.
template <class T>
actor_system_config& set(const char* name, T&& value) {
actor_system_config& set(string_view name, T&& value) {
return set_impl(name, config_value{std::forward<T>(value)});
}
......@@ -403,7 +404,7 @@ private:
&make_type_erased_value<T>);
}
actor_system_config& set_impl(const char* name, config_value value);
actor_system_config& set_impl(string_view name, config_value value);
static std::string render_sec(uint8_t, atom_value, const message&);
......
......@@ -18,9 +18,11 @@
#pragma once
#include <memory>
#include <string>
#include "caf/fwd.hpp"
#include "caf/string_view.hpp"
namespace caf {
......@@ -49,8 +51,8 @@ public:
// -- constructors, destructors, and assignment operators --------------------
/// Constructs a config option.
config_option(std::string category, const char* name, std::string description,
const meta_state* meta, void* storage = nullptr);
config_option(string_view category, string_view name, string_view description,
const meta_state* meta, void* value = nullptr);
config_option(config_option&&) = default;
......@@ -58,25 +60,20 @@ public:
// -- properties -------------------------------------------------------------
inline const std::string& category() const noexcept {
return category_;
}
/// Returns the category of the option.
string_view category() const noexcept;
/// Returns the
inline const std::string& long_name() const noexcept {
return long_name_;
}
/// Returns the name of the option.
string_view long_name() const noexcept;
inline const std::string& short_names() const noexcept {
return short_names_;
}
/// Returns (optional) one-letter short names of the option.
string_view short_names() const noexcept;
inline const std::string& description() const noexcept {
return description_;
}
/// Returns a human-readable description of the option.
string_view description() const noexcept;
/// Returns the full name for this config option as "<category>.<long name>".
std::string full_name() const;
string_view full_name() const noexcept;
/// Checks whether `x` holds a legal value for this option.
error check(const config_value& x) const;
......@@ -86,7 +83,7 @@ public:
void store(const config_value& x) const;
/// Returns a human-readable representation of this option's expected type.
const std::string& type_name() const noexcept;
string_view type_name() const noexcept;
/// Returns whether this config option stores a boolean flag.
bool is_flag() const noexcept;
......@@ -96,10 +93,13 @@ public:
optional<config_value> get() const;
private:
std::string category_;
std::string long_name_;
std::string short_names_;
std::string description_;
string_view buf_slice(size_t from, size_t to) const noexcept;
std::unique_ptr<char[]> buf_;
uint16_t category_separator_;
uint16_t long_name_separator_;
uint16_t short_names_separator_;
uint16_t buf_size_;
const meta_state* meta_;
mutable void* value_;
};
......
......@@ -30,36 +30,36 @@ class config_option_adder {
public:
// -- constructors, destructors, and assignment operators --------------------
config_option_adder(config_option_set& target, const char* category);
config_option_adder(config_option_set& target, string_view category);
// -- properties -------------------------------------------------------------
/// Adds a config option to the category that synchronizes with `ref`.
template <class T>
config_option_adder& add(T& ref, const char* name, const char* description) {
config_option_adder& add(T& ref, string_view name, string_view description) {
return add_impl(make_config_option(ref, category_, name, description));
}
/// Adds a config option to the category.
template <class T>
config_option_adder& add(const char* name, const char* description) {
config_option_adder& add(string_view name, string_view description) {
return add_impl(make_config_option<T>(category_, name, description));
}
/// For backward compatibility only. Do not use for new code!
/// @private
config_option_adder& add_neg(bool& ref, const char* name,
const char* description);
config_option_adder& add_neg(bool& ref, string_view name,
string_view description);
/// For backward compatibility only. Do not use for new code!
/// @private
config_option_adder& add_us(size_t& ref, const char* name,
const char* description);
config_option_adder& add_us(size_t& ref, string_view name,
string_view description);
/// For backward compatibility only. Do not use for new code!
/// @private
config_option_adder& add_ms(size_t& ref, const char* name,
const char* description);
config_option_adder& add_ms(size_t& ref, string_view name,
string_view description);
private:
// -- properties -------------------------------------------------------------
......@@ -72,7 +72,7 @@ private:
config_option_set& xs_;
/// Category for all options generated by this adder.
const char* category_;
string_view category_;
};
} // namespace caf
......@@ -28,6 +28,7 @@
#include "caf/fwd.hpp"
#include "caf/make_config_option.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
namespace caf {
......@@ -53,10 +54,10 @@ public:
using iterator = option_vector::iterator;
/// Maps string keys to arbitrary (config) values.
using dictionary = std::map<std::string, config_value>;
using dictionary = dictionary<config_value>;
/// Categorized settings.
using config_map = std::map<std::string, dictionary>;
using config_map = caf::dictionary<dictionary>;
// -- constructors, destructors, and assignment operators --------------------
......@@ -69,18 +70,18 @@ public:
/// `<prefix>#<category>.<long-name>`. Users can omit `<prefix>`
/// for options that have an empty prefix and `<category>`
/// if the category is "global".
option_pointer cli_long_name_lookup(const std::string& name) const;
option_pointer cli_long_name_lookup(string_view name) const;
/// Returns the first `config_option` that matches the CLI short option name.
option_pointer cli_short_name_lookup(char short_name) const;
/// Returns the first `config_option` that matches category and long name.
option_pointer qualified_name_lookup(const std::string& category,
const std::string& long_name) const;
option_pointer qualified_name_lookup(string_view category,
string_view long_name) const;
/// Returns the first `config_option` that matches the qualified name.
/// @param name Config option name formatted as `<category>.<long-name>`.
option_pointer qualified_name_lookup(const std::string& name) const;
option_pointer qualified_name_lookup(string_view name) const;
/// Returns the number of stored config options.
inline size_t size() const noexcept {
......@@ -99,15 +100,15 @@ public:
/// Adds a config option to the set.
template <class T>
config_option_set& add(const char* category, const char* name,
const char* description = "") {
config_option_set& add(string_view category, string_view name,
string_view description = "") {
return add(make_config_option<T>(category, name, description));
}
/// Adds a config option to the set that synchronizes its value with `ref`.
template <class T>
config_option_set& add(T& ref, const char* category, const char* name,
const char* description = "") {
config_option_set& add(T& ref, string_view category, string_view name,
string_view description = "") {
return add(make_config_option<T>(ref, category, name, description));
}
......
......@@ -27,6 +27,7 @@
#include "caf/atom.hpp"
#include "caf/detail/bounds_checker.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/dictionary.hpp"
#include "caf/fwd.hpp"
#include "caf/optional.hpp"
#include "caf/string_algorithms.hpp"
......@@ -59,7 +60,7 @@ public:
using list = std::vector<config_value>;
using dictionary = std::map<std::string, config_value>;
using dictionary = dictionary<config_value>;
using types = detail::type_list<integer, boolean, real, atom, timespan,
string, list, dictionary>;
......@@ -96,11 +97,11 @@ public:
// -- parsing ----------------------------------------------------------------
/// Tries to parse a value from `str`.
static expected<config_value> parse(std::string::const_iterator first,
std::string::const_iterator last);
static expected<config_value> parse(string_view::iterator first,
string_view::iterator last);
/// Tries to parse a value from `str`.
static expected<config_value> parse(const std::string& str);
static expected<config_value> parse(string_view str);
// -- properties -------------------------------------------------------------
......@@ -175,7 +176,7 @@ private:
/// Organizes config values into categories.
/// @relates config_value
using config_value_map = std::map<std::string, config_value::dictionary>;
using config_value_map = dictionary<config_value::dictionary>;
// -- SumType-like access ------------------------------------------------------
......@@ -351,12 +352,12 @@ struct config_value_access<std::vector<T>> {
}
};
/// Implements automagic unboxing of `std::map<std::string, V>` from a
/// homogeneous `config_value::dictionary`.
/// Implements automagic unboxing of `dictionary<V>` from a homogeneous
/// `config_value::dictionary`.
/// @relates config_value
template <class V>
struct config_value_access<std::map<std::string, V>> {
using map_type = std::map<std::string, V>;
struct config_value_access<dictionary<V>> {
using map_type = dictionary<V>;
using kvp = std::pair<const std::string, config_value>;
......@@ -401,10 +402,9 @@ struct config_value_access<std::map<std::string, V>> {
/// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value
template <class T>
optional<T> get_if(const config_value::dictionary* xs,
const std::string& name) {
optional<T> get_if(const config_value::dictionary* xs, string_view name) {
// Split the name into a path.
std::vector<std::string> path;
std::vector<string_view> path;
split(path, name, ".");
// Sanity check.
if (path.size() == 0)
......@@ -433,7 +433,7 @@ optional<T> get_if(const config_value::dictionary* xs,
/// Retrieves the value associated to `name` from `xs`.
/// @relates config_value
template <class T>
T get(const config_value::dictionary& xs, const std::string& name) {
T get(const config_value::dictionary& xs, string_view name) {
auto result = get_if<T>(&xs, name);
if (result)
return std::move(*result);
......@@ -444,7 +444,7 @@ T get(const config_value::dictionary& xs, const std::string& name) {
/// `default_value`.
/// @relates config_value
template <class T, class E = detail::enable_if_t<!std::is_pointer<T>::value>>
T get_or(const config_value::dictionary& xs, const std::string& name,
T get_or(const config_value::dictionary& xs, string_view name,
const T& default_value) {
auto result = get_if<T>(&xs, name);
if (result)
......@@ -455,13 +455,13 @@ T get_or(const config_value::dictionary& xs, const std::string& name,
/// Retrieves the value associated to `name` from `xs` or returns
/// `default_value`.
/// @relates config_value
std::string get_or(const config_value::dictionary& xs, const std::string& name,
std::string get_or(const config_value::dictionary& xs, string_view name,
const char* default_value);
/// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value
template <class T>
optional<T> get_if(const config_value_map* xs, const std::string& name) {
optional<T> get_if(const config_value_map* xs, string_view name) {
// Get the category.
auto pos = name.find('.');
if (pos == std::string::npos) {
......@@ -479,7 +479,7 @@ optional<T> get_if(const config_value_map* xs, const std::string& name) {
/// Retrieves the value associated to `name` from `xs`.
/// @relates config_value
template <class T>
T get(const config_value_map& xs, const std::string& name) {
T get(const config_value_map& xs, string_view name) {
auto result = get_if<T>(&xs, name);
if (result)
return std::move(*result);
......@@ -490,8 +490,7 @@ T get(const config_value_map& xs, const std::string& name) {
/// `default_value`.
/// @relates config_value
template <class T, class E = detail::enable_if_t<!std::is_pointer<T>::value>>
T get_or(const config_value_map& xs, const std::string& name,
const T& default_value) {
T get_or(const config_value_map& xs, string_view name, const T& default_value) {
auto result = get_if<T>(&xs, name);
if (result)
return std::move(*result);
......@@ -501,20 +500,20 @@ T get_or(const config_value_map& xs, const std::string& name,
/// Retrieves the value associated to `name` from `xs` or returns
/// `default_value`.
/// @relates config_value
std::string get_or(const config_value_map& xs, const std::string& name,
std::string get_or(const config_value_map& xs, string_view name,
const char* default_value);
/// Tries to retrieve the value associated to `name` from `cfg`.
/// @relates config_value
template <class T>
optional<T> get_if(const actor_system_config* cfg, const std::string& name) {
optional<T> get_if(const actor_system_config* cfg, string_view name) {
return get_if<T>(&content(*cfg), name);
}
/// Retrieves the value associated to `name` from `cfg`.
/// @relates config_value
template <class T>
T get(const actor_system_config& cfg, const std::string& name) {
T get(const actor_system_config& cfg, string_view name) {
return get<T>(content(cfg), name);
}
......@@ -522,12 +521,12 @@ T get(const actor_system_config& cfg, const std::string& name) {
/// `default_value`.
/// @relates config_value
template <class T, class E = detail::enable_if_t<!std::is_pointer<T>::value>>
T get_or(const actor_system_config& cfg, const std::string& name,
T get_or(const actor_system_config& cfg, string_view name,
const T& default_value) {
return get_or(content(cfg), name, default_value);
}
std::string get_or(const actor_system_config& cfg, const std::string& name,
std::string get_or(const actor_system_config& cfg, string_view name,
const char* default_value);
/// @relates config_value
bool operator<(const config_value& x, const config_value& y);
......
......@@ -18,11 +18,11 @@
#pragma once
#include <map>
#include <string>
#include "caf/config_option_set.hpp"
#include "caf/config_value.hpp"
#include "caf/dictionary.hpp"
namespace caf {
namespace detail {
......@@ -183,7 +183,7 @@ public:
using super = abstract_ini_consumer;
using config_map = std::map<std::string, config_value::dictionary>;
using config_map = dictionary<config_value::dictionary>;
// -- constructors, destructors, and assignment operators --------------------
......
......@@ -18,11 +18,11 @@
#pragma once
#include <map>
#include <string>
#include <type_traits>
#include <vector>
#include "caf/dictionary.hpp"
#include "caf/fwd.hpp"
#include "caf/timestamp.hpp"
......@@ -127,7 +127,7 @@ struct type_name_builder<std::vector<T>, false> {
};
template <class T>
struct type_name_builder<std::map<std::string, T>, false> {
struct type_name_builder<dictionary<T>, false> {
void operator()(std::string& result) const {
result += "dictionary of ";
type_name_builder<T> g;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <algorithm>
#include <iterator>
#include <map>
#include <string>
#include "caf/string_view.hpp"
namespace caf {
/// Maps strings to values of type `V`, but unlike `std::map<std::string, V>`
/// accepts `string_view` for looking up keys efficiently.
template <class V>
class dictionary {
public:
// -- member types ----------------------------------------------------------
using map_type = std::map<std::string, V>;
using key_type = typename map_type::key_type;
using mapped_type = typename map_type::mapped_type;
using value_type = typename map_type::value_type;
using allocator_type = typename map_type::allocator_type;
using size_type = typename map_type::size_type;
using difference_type = typename map_type::difference_type;
using reference = typename map_type::reference;
using const_reference = typename map_type::const_reference;
using pointer = typename map_type::pointer;
using const_pointer = typename map_type::const_pointer;
using iterator = typename map_type::iterator;
using const_iterator = typename map_type::const_iterator;
using reverse_iterator = typename map_type::reverse_iterator;
using const_reverse_iterator = typename map_type::const_reverse_iterator;
using iterator_bool_pair = std::pair<iterator, bool>;
struct mapped_type_less {
inline bool operator()(const value_type& x, string_view y) const {
return x.first < y;
}
inline bool operator()(const value_type& x, const value_type& y) const {
return x.first < y.first;
}
inline bool operator()(string_view x, const value_type& y) const {
return x < y.first;
}
};
// -- constructors, destructors, and assignment operators -------------------
dictionary() = default;
dictionary(std::initializer_list<value_type> xs) : xs_(xs) {
// nop
}
template <class InputIterator>
dictionary(InputIterator first, InputIterator last) : xs_(first, last) {
// nop
}
// -- iterator access --------------------------------------------------------
iterator begin() noexcept {
return xs_.begin();
}
const_iterator begin() const noexcept {
return xs_.begin();
}
const_iterator cbegin() const noexcept {
return begin();
}
reverse_iterator rbegin() noexcept {
return xs_.rbegin();
}
const_reverse_iterator rbegin() const noexcept {
return xs_.rbegin();
}
const_reverse_iterator crbegin() const noexcept {
return rbegin();
}
iterator end() noexcept {
return xs_.end();
}
const_iterator end() const noexcept {
return xs_.end();
}
const_iterator cend() const noexcept {
return end();
}
reverse_iterator rend() noexcept {
return xs_.rend();
}
const_reverse_iterator rend() const noexcept {
return xs_.rend();
}
const_reverse_iterator crend() const noexcept {
return rend();
}
// -- size -------------------------------------------------------------------
bool empty() const noexcept {
return xs_.empty();
}
size_type size() const noexcept {
return xs_.size();
}
// -- access to members ------------------------------------------------------
/// Gives raw access to the underlying container.
map_type& container() noexcept {
return xs_;
}
/// Gives raw access to the underlying container.
const map_type& container() const noexcept {
return xs_;
}
// -- modifiers -------------------------------------------------------------
void clear() noexcept {
return xs_.clear();
}
void swap(dictionary& other) {
xs_.swap(other.xs_);
}
// -- insertion --------------------------------------------------------------
template <class K, class T>
iterator_bool_pair emplace(K&& key, T&& value) {
auto i = lower_bound(key);
if (i == end())
return xs_.emplace(copy(std::forward<K>(key)), std::forward<T>(value));
if (i->first == key)
return {i, false};
return {xs_.emplace_hint(i, copy(std::forward<K>(key)),
std::forward<T>(value)),
true};
}
template <class T>
iterator_bool_pair insert(string_view key, T&& value) {
return emplace(key, std::forward<T>(value));
}
template <class K, class T>
iterator emplace_hint(iterator hint, K&& key, T&& value) {
if (hint == end() || hint->first > key)
return xs_.emplace(copy(std::forward<K>(key)), std::forward<T>(value))
.first;
if (hint->first == key)
return hint;
return xs_.emplace_hint(hint, copy(std::forward<K>(key)),
std::forward<T>(value));
}
template <class T>
iterator insert(iterator hint, string_view key, T&& value) {
return emplace_hint(hint, key, std::forward<T>(value));
}
template <class T>
iterator_bool_pair insert_or_assign(string_view key, T&& value) {
auto i = lower_bound(key);
if (i == end())
return xs_.emplace(copy(key), std::forward<T>(value));
if (i->first == key) {
i->second = std::forward<T>(value);
return {i, false};
}
return {xs_.emplace_hint(i, copy(key), std::forward<T>(value)), true};
}
template <class T>
iterator insert_or_assign(iterator hint, string_view key, T&& value) {
if (hint == end() || hint->first > key)
return insert_or_assign(key, std::forward<T>(value)).first;
hint = lower_bound(hint, key);
if (hint != end() && hint->first == key) {
hint->second = std::forward<T>(value);
return hint;
}
return xs_.emplace_hint(hint, copy(key), std::forward<T>(value));
}
// -- lookup -----------------------------------------------------------------
bool contains(string_view key) const noexcept {
auto i = lower_bound(key);
return !(i == end() || i->first != key);
}
size_t count(string_view key) const noexcept {
return contains(key) ? 1u : 0u;
}
iterator find(string_view key) noexcept {
auto i = lower_bound(key);
return i != end() && i->first == key ? i : end();
}
const_iterator find(string_view key) const noexcept {
auto i = lower_bound(key);
return i != end() && i->first == key ? i : end();
}
iterator lower_bound(string_view key) {
return lower_bound(begin(), key);
}
const_iterator lower_bound(string_view key) const {
return lower_bound(begin(), key);
}
iterator upper_bound(string_view key) {
mapped_type_less cmp;
return std::upper_bound(begin(), end(), key, cmp);
}
const_iterator upper_bound(string_view key) const {
mapped_type_less cmp;
return std::upper_bound(begin(), end(), key, cmp);
}
// -- element access ---------------------------------------------------------
mapped_type& operator[](string_view key) {
return insert(key, mapped_type{}).first->second;
}
private:
iterator lower_bound(iterator from, string_view key) {
mapped_type_less cmp;
return std::lower_bound(from, end(), key, cmp);
}
const_iterator lower_bound(const_iterator from, string_view key) const {
mapped_type_less cmp;
return std::lower_bound(from, end(), key, cmp);
}
// Copies the content of `str` into a new string.
static inline std::string copy(string_view str) {
return std::string{str.begin(), str.end()};
}
// Moves the content of `str` into a new string.
static inline std::string copy(std::string str) {
return str;
}
map_type xs_;
};
// @relates dictionary
template <class T>
bool operator==(const dictionary<T>& xs, const dictionary<T>& ys) {
return xs.container() == ys.container();
}
// @relates dictionary
template <class T>
bool operator!=(const dictionary<T>& xs, const dictionary<T>& ys) {
return xs.container() != ys.container();
}
// @relates dictionary
template <class T>
bool operator<(const dictionary<T>& xs, const dictionary<T>& ys) {
return xs.container() < ys.container();
}
// @relates dictionary
template <class T>
bool operator<=(const dictionary<T>& xs, const dictionary<T>& ys) {
return xs.container() <= ys.container();
}
// @relates dictionary
template <class T>
bool operator>(const dictionary<T>& xs, const dictionary<T>& ys) {
return xs.container() > ys.container();
}
// @relates dictionary
template <class T>
bool operator>=(const dictionary<T>& xs, const dictionary<T>& ys) {
return xs.container() >= ys.container();
}
} // namespace caf
......@@ -32,6 +32,7 @@ namespace caf {
// -- 1 param templates --------------------------------------------------------
template <class> class behavior_type_of;
template <class> class dictionary;
template <class> class downstream;
template <class> class expected;
template <class> class intrusive_ptr;
......@@ -119,6 +120,7 @@ class scheduled_actor;
class scoped_actor;
class serializer;
class stream_manager;
class string_view;
class type_erased_tuple;
class type_erased_value;
class uniform_type_info_map;
......@@ -142,12 +144,12 @@ struct prohibit_top_level_spawn_marker;
// -- free template functions --------------------------------------------------
template <class T>
config_option make_config_option(const char* category, const char* name,
const char* description);
config_option make_config_option(string_view category, string_view name,
string_view description);
template <class T>
config_option make_config_option(T& storage, const char* category,
const char* name, const char* description);
config_option make_config_option(T& storage, string_view category,
string_view name, string_view description);
// -- enums --------------------------------------------------------------------
......@@ -162,8 +164,7 @@ using stream_slot = uint16_t;
// -- functions ----------------------------------------------------------------
/// @relates actor_system_config
const std::map<std::string, std::map<std::string, config_value>>&
content(const actor_system_config&);
const dictionary<dictionary<config_value>>& content(const actor_system_config&);
// -- intrusive containers -----------------------------------------------------
......
......@@ -50,15 +50,15 @@ config_option::meta_state* option_meta_state_instance() {
/// Creates a config option that synchronizes with `storage`.
template <class T>
config_option make_config_option(const char* category, const char* name,
const char* description) {
config_option make_config_option(string_view category, string_view name,
string_view description) {
return {category, name, description, detail::option_meta_state_instance<T>()};
}
/// Creates a config option that synchronizes with `storage`.
template <class T>
config_option make_config_option(T& storage, const char* category,
const char* name, const char* description) {
config_option make_config_option(T& storage, string_view category,
string_view name, string_view description) {
return {category, name, description, detail::option_meta_state_instance<T>(),
std::addressof(storage)};
}
......@@ -66,21 +66,21 @@ config_option make_config_option(T& storage, const char* category,
// -- backward compatbility, do not use for new code ! -------------------------
// Inverts the value when writing to `storage`.
config_option make_negated_config_option(bool& storage, const char* category,
const char* name,
const char* description);
config_option make_negated_config_option(bool& storage, string_view category,
string_view name,
string_view description);
// Reads timespans, but stores an integer representing microsecond resolution.
config_option make_us_resolution_config_option(size_t& storage,
const char* category,
const char* name,
const char* description);
string_view category,
string_view name,
string_view description);
// Reads timespans, but stores an integer representing millisecond resolution.
config_option make_ms_resolution_config_option(size_t& storage,
const char* category,
const char* name,
const char* description);
string_view category,
string_view name,
string_view description);
// -- specializations for common types -----------------------------------------
......
......@@ -39,8 +39,8 @@ inline std::string is_any_of(std::string arg) {
constexpr bool token_compress_on = false;
template <class Container, class Delim>
void split(Container& result, const std::string& str, const Delim& delims,
template <class Container, class Str, class Delim>
void split(Container& result, const Str& str, const Delim& delims,
bool keep_all = true) {
size_t pos = 0;
size_t prev = 0;
......@@ -53,9 +53,8 @@ void split(Container& result, const std::string& str, const Delim& delims,
}
prev = pos + 1;
}
if (prev < str.size()) {
if (prev < str.size())
result.push_back(str.substr(prev, std::string::npos));
}
}
template <class Iterator>
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include <iosfwd>
#include <iterator>
#include <limits>
#include <type_traits>
#include "caf/detail/comparable.hpp"
namespace caf {
namespace detail {
// Catches `std::string`, `std::string_view` and all classes mimicing those,
// but not `std::vector<char>` or other buffers.
template <class T>
struct is_string_like {
// SFINAE checks.
template <class U>
static bool sfinae(
const U* x,
// check if `(*x)[0]` returns `const char&`
typename std::enable_if<
std::is_same<
const char&,
decltype((*x)[0])
>::value
>::type* = nullptr,
// check if `x->size()` returns an integer
typename std::enable_if<
std::is_integral<
decltype(x->size())
>::value
>::type* = nullptr,
// check if `x->compare(*x)` is well-formed and returns an integer
// (distinguishes vectors from strings)
typename std::enable_if<
std::is_integral<
decltype(x->compare(*x))
>::value
>::type* = nullptr);
// SFINAE fallback.
static void sfinae(void*);
// Result of SFINAE test.
using result_type =
decltype(sfinae(static_cast<typename std::decay<T>::type*>(nullptr)));
// Trait result.
static constexpr bool value = std::is_same<bool, result_type>::value;
};
} // namespace detail
/// Drop-in replacement for C++17 std::string_view.
class string_view : detail::comparable<string_view> {
public:
// -- member types -----------------------------------------------------------
using value_type = char;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using const_iterator = const_pointer;
using iterator = const_iterator;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using reverse_iterator = const_reverse_iterator;
using size_type = size_t;
using difference_type = ptrdiff_t;
// -- constants --------------------------------------------------------------
static constexpr size_type npos = std::numeric_limits<size_type>::max();
// -- constructors, destructors, and assignment operators --------------------
constexpr string_view() noexcept : data_(nullptr), size_(0) {
// nop
}
constexpr string_view(const char* cstr, size_t length) noexcept
: data_(cstr),
size_(length) {
// nop
}
template <size_t N>
constexpr string_view(const char (&cstr)[N]) noexcept
: data_(cstr),
size_(N - 1) {
static_assert(N > 0, "");
}
constexpr string_view(const string_view&) noexcept = default;
template <
class T,
class = typename std::enable_if<detail::is_string_like<T>::value>::type
>
string_view(const T& str) noexcept {
auto len = str.size();
if (len == 0) {
data_ = nullptr;
size_ = 0;
} else {
data_ = &(str[0]);
size_ = str.size();
}
}
string_view& operator=(const string_view&) noexcept = default;
// -- capacity ---------------------------------------------------------------
constexpr size_type size() const noexcept {
return size_;
}
constexpr size_type length() const noexcept {
return size_;
}
constexpr size_type max_size() const noexcept {
return std::numeric_limits<size_type>::max();
}
constexpr bool empty() const noexcept {
return size_ == 0;
}
// -- iterator access --------------------------------------------------------
constexpr const_iterator begin() const noexcept {
return data_;
}
constexpr const_iterator end() const noexcept {
return data_ + size_;
}
constexpr const_iterator cbegin() const noexcept {
return begin();
}
constexpr const_iterator cend() const noexcept {
return end();
}
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator rend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
// -- element access ---------------------------------------------------------
constexpr const_reference operator[](size_type pos) const {
return data_[pos];
}
const_reference at(size_type pos) const;
constexpr const_reference front() const {
return *data_;
}
constexpr const_reference back() const {
return data_[size_ - 1];
}
constexpr const_pointer data() const noexcept {
return data_;
}
// -- modifiers --------------------------------------------------------------
void remove_prefix(size_type n);
void remove_suffix(size_type n);
void assign(const_pointer data, size_type len);
// -- algortihms -------------------------------------------------------------
size_type copy(pointer dest, size_type n, size_type pos = 0) const;
string_view substr(size_type pos = 0, size_type n = npos) const noexcept;
int compare(string_view s) const noexcept;
int compare(size_type pos1, size_type n1, string_view str) const noexcept;
int compare(size_type pos1, size_type n1, string_view str,
size_type pos2, size_type n2) const noexcept;
int compare(const_pointer str) const noexcept;
int compare(size_type pos, size_type n, const_pointer str) const noexcept;
int compare(size_type pos1, size_type n1,
const_pointer s, size_type n2) const noexcept;
size_type find(string_view str, size_type pos = 0) const noexcept;
size_type find(value_type ch, size_type pos = 0) const noexcept;
size_type find(const_pointer str, size_type pos, size_type n) const noexcept;
size_type find(const_pointer str, size_type pos = 0) const noexcept;
size_type rfind(string_view str, size_type pos = npos) const noexcept;
size_type rfind(value_type ch, size_type pos = npos) const noexcept;
size_type rfind(const_pointer str, size_type pos, size_type n) const noexcept;
size_type rfind(const_pointer str, size_type pos = npos) const noexcept;
size_type find_first_of(string_view str, size_type pos = 0) const noexcept;
size_type find_first_of(value_type ch, size_type pos = 0) const noexcept;
size_type find_first_of(const_pointer str, size_type pos,
size_type n) const noexcept;
size_type find_first_of(const_pointer str, size_type pos = 0) const noexcept;
size_type find_last_of(string_view str, size_type pos = npos) const noexcept;
size_type find_last_of(value_type ch, size_type pos = npos) const noexcept;
size_type find_last_of(const_pointer str, size_type pos,
size_type n) const noexcept;
size_type find_last_of(const_pointer str,
size_type pos = npos) const noexcept;
size_type find_first_not_of(string_view str,
size_type pos = 0) const noexcept;
size_type find_first_not_of(value_type ch, size_type pos = 0) const noexcept;
size_type find_first_not_of(const_pointer str, size_type pos,
size_type n) const noexcept;
size_type find_first_not_of(const_pointer str,
size_type pos = 0) const noexcept;
size_type find_last_not_of(string_view str,
size_type pos = npos) const noexcept;
size_type find_last_not_of(value_type ch,
size_type pos = npos) const noexcept;
size_type find_last_not_of(const_pointer str, size_type pos,
size_type n) const noexcept;
size_type find_last_not_of(const_pointer str,
size_type pos = npos) const noexcept;
private:
const char* data_;
size_t size_;
};
} // namespace caf
namespace std {
std::ostream& operator<<(std::ostream& out, caf::string_view);
} // namespace std
......@@ -388,7 +388,7 @@ actor_system_config::add_error_category(atom_value x, error_renderer y) {
return *this;
}
actor_system_config& actor_system_config::set_impl(const char* name,
actor_system_config& actor_system_config::set_impl(string_view name,
config_value value) {
auto opt = custom_options_.qualified_name_lookup(name);
if (opt != nullptr && opt->check(value) == none) {
......@@ -454,7 +454,7 @@ void actor_system_config::extract_config_file_path(string_list& args) {
args.erase(i);
}
const std::map<std::string, std::map<std::string, config_value>>&
const dictionary<dictionary<config_value>>&
content(const actor_system_config& cfg) {
return cfg.content;
}
......
......@@ -18,8 +18,9 @@
#include "caf/config_option.hpp"
#include <cstring>
#include <iostream>
#include <algorithm>
#include <limits>
#include <numeric>
#include "caf/config.hpp"
#include "caf/config_value.hpp"
......@@ -29,41 +30,73 @@
using std::move;
using std::string;
namespace {
namespace caf {
// -- constructors, destructors, and assignment operators ----------------------
string get_long_name(const char* name) {
auto i= strchr(name, ',');
return i != nullptr ? string{name, i} : string{name};
config_option::config_option(string_view category, string_view name,
string_view description, const meta_state* meta,
void* value)
: meta_(meta),
value_(value) {
using std::copy;
using std::accumulate;
auto comma = name.find(',');
auto long_name = name.substr(0, comma);
auto short_names = comma == string_view::npos ? string_view{}
: name.substr(comma + 1);
auto total_size = [](std::initializer_list<string_view> xs) {
return (xs.size() - 1) // one separator between all fields
+ accumulate(xs.begin(), xs.end(), size_t{0},
[](size_t x, string_view sv) { return x + sv.size(); });
};
auto ts = total_size({category, long_name, short_names, description});
CAF_ASSERT(ts <= std::numeric_limits<uint16_t>::max());
buf_size_ = static_cast<uint16_t>(ts);
buf_.reset(new char[ts]);
// fille the buffer with "<category>.<long-name>,<short-name>,<descriptions>"
auto first = buf_.get();
auto i = first;
auto pos = [&] {
return static_cast<uint16_t>(std::distance(first, i));
};
// <category>.
i = copy(category.begin(), category.end(), i);
category_separator_ = pos();
*i++ = '.';
// <long-name>,
i = copy(long_name.begin(), long_name.end(), i);
long_name_separator_ = pos();
*i++ = ',';
// <short-names>,
i = copy(short_names.begin(), short_names.end(), i);
short_names_separator_ = pos();
*i++ = ',';
// <description>
copy(description.begin(), description.end(), i);
CAF_ASSERT(pos() == buf_size_);
}
string get_short_names(const char* name) {
auto substr = strchr(name, ',');
if (substr != nullptr)
return ++substr;
return string{};
// -- properties ---------------------------------------------------------------
string_view config_option::category() const noexcept {
return buf_slice(0, category_separator_);
}
} // namespace <anonymous>
string_view config_option::long_name() const noexcept {
return buf_slice(category_separator_ + 1, long_name_separator_);
}
namespace caf {
string_view config_option::short_names() const noexcept {
return buf_slice(long_name_separator_ + 1, short_names_separator_);
}
config_option::config_option(string category, const char* name,
string description, const meta_state* meta,
void* value)
: category_(move(category)),
long_name_(get_long_name(name)),
short_names_(get_short_names(name)),
description_(move(description)),
meta_(meta),
value_(value) {
CAF_ASSERT(meta_ != nullptr);
string_view config_option::description() const noexcept {
return buf_slice(short_names_separator_ + 1, buf_size_);
}
string config_option::full_name() const {
std::string result = category();
result += '.';
result += long_name_;
return result;
string_view config_option::full_name() const noexcept {
return buf_slice(0, long_name_separator_);
}
error config_option::check(const config_value& x) const {
......@@ -78,7 +111,7 @@ void config_option::store(const config_value& x) const {
}
}
const std::string& config_option::type_name() const noexcept {
string_view config_option::type_name() const noexcept {
return meta_->type_name;
}
......@@ -92,4 +125,9 @@ optional<config_value> config_option::get() const {
return none;
}
string_view config_option::buf_slice(size_t from, size_t to) const noexcept {
CAF_ASSERT(from <= to);
return {buf_.get() + from, to - from};
}
} // namespace caf
......@@ -23,26 +23,26 @@
namespace caf {
config_option_adder::config_option_adder(config_option_set& target,
const char* category)
string_view category)
: xs_(target),
category_(category) {
// nop
}
config_option_adder& config_option_adder::add_neg(bool& ref, const char* name,
const char* description) {
config_option_adder& config_option_adder::add_neg(bool& ref, string_view name,
string_view description) {
return add_impl(make_negated_config_option(ref, category_,
name, description));
}
config_option_adder& config_option_adder::add_us(size_t& ref, const char* name,
const char* description) {
config_option_adder& config_option_adder::add_us(size_t& ref, string_view name,
string_view description) {
return add_impl(make_us_resolution_config_option(ref, category_,
name, description));
}
config_option_adder& config_option_adder::add_ms(size_t& ref, const char* name,
const char* description) {
config_option_adder& config_option_adder::add_ms(size_t& ref, string_view name,
string_view description) {
return add_impl(make_ms_resolution_config_option(ref, category_,
name, description));
}
......
......@@ -34,19 +34,13 @@ struct string_builder {
std::string result;
};
template <size_t N>
string_builder& operator<<(string_builder& builder, const char (&cstr)[N]) {
builder.result.append(cstr, N - 1);
string_builder& operator<<(string_builder& builder, char ch) {
builder.result += ch;
return builder;
}
string_builder& operator<<(string_builder& builder, char c) {
builder.result += c;
return builder;
}
string_builder& operator<<(string_builder& builder, const std::string& str) {
builder.result += str;
string_builder& operator<<(string_builder& builder, caf::string_view str) {
builder.result.append(str.data(), str.size());
return builder;
}
......@@ -94,8 +88,8 @@ std::string config_option_set::help_text(bool global_only) const {
};
// Sort argument + description by category.
using pair = std::pair<std::string, option_pointer>;
std::set<std::string> categories;
std::multimap<std::string, pair> args;
std::set<string_view> categories;
std::multimap<string_view, pair> args;
size_t max_arg_size = 0;
for (auto& opt : opts_) {
if (!global_only || opt.category() == "global") {
......@@ -132,7 +126,7 @@ auto config_option_set::parse(config_map& config, argument_iterator first,
auto consume = [&](const config_option& opt, iter arg_begin, iter arg_end) {
// Extract option name and category.
auto opt_name = opt.long_name();
string opt_ctg = opt.category();
auto opt_ctg = opt.category();
// Try inserting a new submap into the config or fill existing one.
auto& submap = config[opt_ctg];
// Flags only consume the current element.
......@@ -145,7 +139,9 @@ auto config_option_set::parse(config_map& config, argument_iterator first,
} else {
if (arg_begin == arg_end)
return pec::missing_argument;
auto val = config_value::parse(arg_begin, arg_end);
auto slice_size = static_cast<size_t>(std::distance(arg_begin, arg_end));
string_view slice{&*arg_begin, slice_size};
auto val = config_value::parse(slice);
if (!val)
return pec::illegal_argument;
if (opt.check(*val) != none)
......@@ -224,12 +220,12 @@ config_option_set::parse(config_map& config,
}
config_option_set::option_pointer
config_option_set::cli_long_name_lookup(const string& name) const {
config_option_set::cli_long_name_lookup(string_view name) const {
// We accept "caf#" prefixes for backward compatibility, but ignore them.
size_t offset = name.compare(0, 4, "caf#") != 0 ? 0u : 4u;
// Extract category and long name.
string category;
string long_name;
string_view category;
string_view long_name;
auto sep = name.find('.', offset);
if (sep == string::npos) {
category = "global";
......@@ -255,15 +251,15 @@ config_option_set::cli_short_name_lookup(char short_name) const {
}
config_option_set::option_pointer
config_option_set::qualified_name_lookup(const std::string& category,
const std::string& long_name) const {
config_option_set::qualified_name_lookup(string_view category,
string_view long_name) const {
return detail::ptr_find_if(opts_, [&](const config_option& opt) {
return opt.category() == category && opt.long_name() == long_name;
});
}
config_option_set::option_pointer
config_option_set::qualified_name_lookup(const std::string& name) const {
config_option_set::qualified_name_lookup(string_view name) const {
auto sep = name.find('.');
if (sep == string::npos)
return nullptr;
......
......@@ -50,8 +50,8 @@ config_value::~config_value() {
// -- parsing ------------------------------------------------------------------
expected<config_value> config_value::parse(std::string::const_iterator first,
std::string::const_iterator last) {
expected<config_value> config_value::parse(string_view::iterator first,
string_view::iterator last) {
using namespace detail;
auto i = first;
// Sanity check.
......@@ -62,7 +62,7 @@ expected<config_value> config_value::parse(std::string::const_iterator first,
if (++i == last)
return make_error(pec::unexpected_eof);
// Dispatch to parser.
parser::state<std::string::const_iterator> res;
parser::state<string_view::iterator> res;
detail::ini_value_consumer f;
res.i = i;
res.e = last;
......@@ -84,7 +84,7 @@ expected<config_value> config_value::parse(std::string::const_iterator first,
}
}
expected<config_value> config_value::parse(const std::string& str) {
expected<config_value> config_value::parse(string_view str) {
return parse(str.begin(), str.end());
}
......@@ -132,15 +132,15 @@ std::string get_or(const config_value::dictionary& xs, const std::string& name,
return default_value;
}
std::string get_or(const std::map<std::string, config_value::dictionary>& xs,
const std::string& name, const char* default_value) {
std::string get_or(const dictionary<config_value::dictionary>& xs,
string_view name, const char* default_value) {
auto result = get_if<std::string>(&xs, name);
if (result)
return std::move(*result);
return default_value;
}
std::string get_or(const actor_system_config& cfg, const std::string& name,
std::string get_or(const actor_system_config& cfg, string_view name,
const char* default_value) {
return get_or(content(cfg), name, default_value);
}
......
......@@ -129,23 +129,23 @@ config_option::meta_state bool_meta_state{
} // namespace detail
config_option make_negated_config_option(bool& storage, const char* category,
const char* name,
const char* description) {
config_option make_negated_config_option(bool& storage, string_view category,
string_view name,
string_view description) {
return {category, name, description, &bool_neg_meta, &storage};
}
config_option make_us_resolution_config_option(size_t& storage,
const char* category,
const char* name,
const char* description) {
string_view category,
string_view name,
string_view description) {
return {category, name, description, &us_res_meta, &storage};
}
config_option make_ms_resolution_config_option(size_t& storage,
const char* category,
const char* name,
const char* description) {
string_view category,
string_view name,
string_view description) {
return {category, name, description, &ms_res_meta, &storage};
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/string_view.hpp"
#include <algorithm>
#include <cstring>
#include <iostream>
#include <stdexcept>
#include "caf/config.hpp"
namespace {
using size_type = caf::string_view::size_type;
} // namespace anonymous
namespace caf {
// -- iterator access ----------------------------------------------------------
string_view::const_reverse_iterator string_view::rbegin() const noexcept {
return reverse_iterator{end()};
}
string_view::const_reverse_iterator string_view::rend() const noexcept {
return reverse_iterator{begin()};
}
string_view::const_reverse_iterator string_view::crbegin() const noexcept {
return rbegin();
}
string_view::const_reverse_iterator string_view::crend() const noexcept {
return rend();
}
// -- element access -----------------------------------------------------------
string_view::const_reference string_view::at(size_type pos) const {
if (pos < size_)
return data_[pos];
CAF_RAISE_ERROR("string_view::at out of range");
}
// -- modifiers ----------------------------------------------------------------
void string_view::remove_prefix(size_type n) {
if (n < size()) {
data_ += n;
size_ -= n;
} else {
size_ = 0;
}
}
void string_view::remove_suffix(size_type n) {
if (n < size())
size_ -= n;
else
size_ = 0;
}
void string_view::assign(const_pointer data, size_type len) {
data_ = data;
size_ = len;
}
// -- algortihms ---------------------------------------------------------------
string_view::size_type string_view::copy(pointer dest, size_type n,
size_type pos) const {
if (pos > size_)
CAF_RAISE_ERROR("string_view::copy out of range");
auto first = begin() + pos;
auto end = first + std::min(n, size() - pos);
auto cpy_end = std::copy(first, end, dest);
return static_cast<size_type>(std::distance(dest, cpy_end));
}
string_view string_view::substr(size_type pos, size_type n) const noexcept {
if (pos >= size())
return {};
return {data_ + pos, std::min(size_ - pos, n)};
}
int string_view::compare(string_view str) const noexcept {
return strncmp(data(), str.data(), std::min(size(), str.size()));
}
int string_view::compare(size_type pos1, size_type n1,
string_view str) const noexcept {
return substr(pos1, n1).compare(str);
}
int string_view::compare(size_type pos1, size_type n1, string_view str,
size_type pos2, size_type n2) const noexcept {
return substr(pos1, n1).compare(str.substr(pos2, n2));
}
int string_view::compare(const_pointer str) const noexcept {
return strncmp(data(), str, size());
}
int string_view::compare(size_type pos, size_type n,
const_pointer str) const noexcept {
return substr(pos, n).compare(str);
}
int string_view::compare(size_type pos1, size_type n1,
const_pointer str, size_type n2) const noexcept {
return substr(pos1, n1).compare(string_view{str, n2});
}
size_type string_view::find(string_view str, size_type pos) const noexcept {
string_view tmp;
if (pos < size_)
tmp.assign(data_ + pos, size_ - pos);
auto first = tmp.begin();
auto last = tmp.end();
auto i = std::search(first, last, str.begin(), str.end());
if (i != last)
return static_cast<size_type>(std::distance(first, i)) + pos;
return npos;
}
size_type string_view::find(value_type ch, size_type pos) const noexcept {
return find(string_view{&ch, 1}, pos);
}
size_type string_view::find(const_pointer str, size_type pos,
size_type n) const noexcept {
return find(string_view{str, n}, pos);
}
size_type string_view::find(const_pointer str, size_type pos) const noexcept {
return find(string_view{str, strlen(str)}, pos);
}
size_type string_view::rfind(string_view str, size_type pos) const noexcept {
if (size() < str.size())
return npos;
if (str.empty())
return std::min(size(), pos);
string_view tmp{data_, std::min(size() - str.size(), pos) + str.size()};
auto first = tmp.begin();
auto last = tmp.end();
auto i = std::find_end(first, last, str.begin(), str.end());
return i != last ? static_cast<size_type>(std::distance(first, i)) : npos;
}
size_type string_view::rfind(value_type ch, size_type pos) const noexcept {
return rfind(string_view{&ch, 1}, pos);
}
size_type string_view::rfind(const_pointer str, size_type pos,
size_type n) const noexcept {
return rfind(string_view{str, n}, pos);
}
size_type string_view::rfind(const_pointer str, size_type pos) const noexcept {
return rfind(string_view{str, strlen(str)}, pos);
}
size_type string_view::find_first_of(string_view str,
size_type pos) const noexcept {
if (empty() || str.empty())
return npos;
if (pos >= size())
return npos;
if (str.size() == 1)
return find(str.front(), pos);
string_view tmp{data_ + pos, size_ - pos};
auto first = tmp.begin();
auto last = tmp.end();
auto i = std::find_first_of(first, last, str.begin(), str.end());
return i != last ? static_cast<size_type>(std::distance(first, i)) + pos
: npos;
}
size_type string_view::find_first_of(value_type ch,
size_type pos) const noexcept {
return find(ch, pos);
}
size_type string_view::find_first_of(const_pointer str, size_type pos,
size_type n) const noexcept {
return find_first_of(string_view{str, n}, pos);
}
size_type string_view::find_first_of(const_pointer str,
size_type pos) const noexcept {
return find_first_of(string_view{str, strlen(str)}, pos);
}
size_type string_view::find_last_of(string_view str,
size_type pos) const noexcept {
string_view tmp{data_, pos < size_ ? pos + 1 : size_};
auto result = tmp.find_first_of(str);
if (result == npos)
return npos;
for (;;) {
auto next = tmp.find_first_of(str, result + 1);
if (next == npos)
return result;
result = next;
}
}
size_type string_view::find_last_of(value_type ch,
size_type pos) const noexcept {
return rfind(ch, pos);
}
size_type string_view::find_last_of(const_pointer str, size_type pos,
size_type n) const noexcept {
return find_last_of(string_view{str, n}, pos);
}
size_type string_view::find_last_of(const_pointer str,
size_type pos) const noexcept {
return find_last_of(string_view{str, strlen(str)}, pos);
}
size_type string_view::find_first_not_of(string_view str,
size_type pos) const noexcept {
if (str.size() == 1)
return find_first_not_of(str.front(), pos);
if (pos >= size())
return npos;
auto pred = [&](value_type x) { return str.find(x) == npos; };
string_view tmp{data_ + pos, size_ - pos};
auto first = tmp.begin();
auto last = tmp.end();
auto i = std::find_if(first, last, pred);
return i != last ? static_cast<size_type>(std::distance(first, i)) + pos
: npos;
}
size_type string_view::find_first_not_of(value_type ch,
size_type pos) const noexcept {
if (pos >= size())
return npos;
auto pred = [=](value_type x) { return x != ch; };
string_view tmp{data_ + pos, size_ - pos};
auto first = tmp.begin();
auto last = tmp.end();
auto i = std::find_if(first, last, pred);
return i != last ? static_cast<size_type>(std::distance(first, i)) + pos
: npos;
}
size_type string_view::find_first_not_of(const_pointer str, size_type pos,
size_type n) const noexcept {
return find_first_not_of(string_view{str, n}, pos);
}
size_type string_view::find_first_not_of(const_pointer str,
size_type pos) const noexcept {
return find_first_not_of(string_view{str, strlen(str)}, pos);
}
size_type string_view::find_last_not_of(string_view str,
size_type pos) const noexcept {
string_view tmp{data_, pos < size_ ? pos + 1 : size_};
auto result = tmp.find_first_not_of(str);
if (result == npos)
return npos;
for (;;) {
auto next = tmp.find_first_not_of(str, result + 1);
if (next == npos)
return result;
result = next;
}
}
size_type string_view::find_last_not_of(value_type ch,
size_type pos) const noexcept {
return find_last_not_of(string_view{&ch, 1}, pos);
}
size_type string_view::find_last_not_of(const_pointer str, size_type pos,
size_type n) const noexcept {
return find_last_not_of(string_view{str, n}, pos);
}
size_type string_view::find_last_not_of(const_pointer str,
size_type pos) const noexcept {
return find_last_not_of(string_view{str, strlen(str)}, pos);
}
} // namespace caf
namespace std {
std::ostream& operator<<(std::ostream& out, caf::string_view str) {
for (auto ch : str)
out.put(ch);
return out;
}
} // namespace std
......@@ -30,9 +30,9 @@ using std::string;
namespace {
constexpr const char* category = "category";
constexpr const char* name = "name";
constexpr const char* explanation = "explanation";
constexpr string_view category = "category";
constexpr string_view name = "name";
constexpr string_view explanation = "explanation";
template<class T>
constexpr int64_t overflow() {
......@@ -45,7 +45,7 @@ constexpr int64_t underflow() {
}
template <class T>
optional<T> read(const std::string& arg) {
optional<T> read(string_view arg) {
auto co = make_config_option<T>(category, name, explanation);
auto res = config_value::parse(arg);
if (res && holds_alternative<T>(*res)) {
......
......@@ -62,7 +62,7 @@ CAF_TEST(lookup) {
CAF_TEST(parse with ref syncing) {
using ls = vector<string>; // list of strings
using ds = std::map<string, string>; // dictionary of strings
using ds = dictionary<string>; // dictionary of strings
auto foo_i = 0;
auto foo_f = 0.f;
auto foo_b = false;
......@@ -74,8 +74,8 @@ CAF_TEST(parse with ref syncing) {
.add<bool>(foo_b, "foo", "b,b", "")
.add<string>(bar_s, "bar", "s,s", "")
.add<vector<string>>(bar_l, "bar", "l,l", "")
.add<std::map<string, string>>(bar_d, "bar", "d,d", "");
std::map<std::string, config_value::dictionary> cfg;
.add<dictionary<string>>(bar_d, "bar", "d,d", "");
dictionary<config_value::dictionary> cfg;
vector<string> args{"-i42",
"-f",
"1e12",
......@@ -103,7 +103,7 @@ CAF_TEST(parse with ref syncing) {
CAF_TEST(implicit global) {
opts.add<int>("global", "value").add<bool>("global", "help");
CAF_MESSAGE("test long option with argument");
std::map<std::string, config_value::dictionary> cfg;
dictionary<config_value::dictionary> cfg;
auto res = opts.parse(cfg, {"--value=42"});
CAF_CHECK_EQUAL(res.first, pec::success);
CAF_CHECK_EQUAL(get_if<int>(&cfg, "global.value"), 42);
......
......@@ -31,8 +31,10 @@
#include "caf/none.hpp"
#include "caf/pec.hpp"
#include "caf/variant.hpp"
#include "caf/string_view.hpp"
using std::string;
using namespace std;
using namespace caf;
namespace {
......@@ -44,7 +46,7 @@ using dictionary = config_value::dictionary;
struct dictionary_builder {
dictionary dict;
dictionary_builder&& add(const char* key, config_value value) && {
dictionary_builder&& add(string_view key, config_value value) && {
dict.emplace(key, std::move(value));
return std::move(*this);
}
......@@ -69,8 +71,8 @@ config_value cfg_lst(Ts&&... xs) {
}
// TODO: switch to std::operator""s when switching to C++14
std::string operator"" _s(const char* str, size_t size) {
return std::string(str, size);
string operator"" _s(const char* str, size_t size) {
return string(str, size);
}
} // namespace <anonymous>
......@@ -158,7 +160,7 @@ CAF_TEST(append) {
}
CAF_TEST(homogeneous dictionary) {
using integer_map = std::map<std::string, int64_t>;
using integer_map = caf::dictionary<int64_t>;
auto xs = dict()
.add("value-1", config_value{100000})
.add("value-2", config_value{2})
......@@ -182,7 +184,7 @@ CAF_TEST(homogeneous dictionary) {
}
CAF_TEST(heterogeneous dictionary) {
using string_list = std::vector<std::string>;
using string_list = std::vector<string>;
auto xs = dict()
.add("scheduler", dict()
.add("policy", config_value{atom("none")})
......@@ -206,7 +208,7 @@ CAF_TEST(successful parsing) {
// references when comparing values. Since we call get<T>() on the result of
// parse(), we would end up with a reference to a temporary.
config_value parsed;
auto parse = [&](const std::string& str) -> config_value& {
auto parse = [&](const string& str) -> config_value& {
auto x = config_value::parse(str);
if (!x)
CAF_FAIL("cannot parse " << str << ": assumed a result but error "
......@@ -214,7 +216,7 @@ CAF_TEST(successful parsing) {
parsed = std::move(*x);
return parsed;
};
using di = std::map<string, int>; // Dictionary-of-integers.
using di = caf::dictionary<int>; // Dictionary-of-integers.
using ls = std::vector<string>; // List-of-strings.
using li = std::vector<int>; // List-of-integers.
using lli = std::vector<li>; // List-of-list-of-integers.
......@@ -235,7 +237,7 @@ CAF_TEST(successful parsing) {
}
CAF_TEST(unsuccessful parsing) {
auto parse = [](const std::string& str) {
auto parse = [](const string& str) {
auto x = config_value::parse(str);
if (x)
CAF_FAIL("assumed an error but got a result");
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE dictionary
#include "caf/test/dsl.hpp"
#include "caf/dictionary.hpp"
using namespace caf;
namespace {
using int_dict = dictionary<int>;
struct fixture {
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(dictionary_tests, fixture)
CAF_TEST(construction and comparions) {
int_dict xs;
CAF_CHECK_EQUAL(xs.empty(), true);
CAF_CHECK_EQUAL(xs.size(), 0u);
int_dict ys{{"foo", 1}, {"bar", 2}};
CAF_CHECK_EQUAL(ys.empty(), false);
CAF_CHECK_EQUAL(ys.size(), 2u);
CAF_CHECK_NOT_EQUAL(xs, ys);
int_dict zs{ys.begin(), ys.end()};
CAF_CHECK_EQUAL(zs.empty(), false);
CAF_CHECK_EQUAL(zs.size(), 2u);
CAF_CHECK_EQUAL(ys, zs);
zs.clear();
CAF_CHECK_EQUAL(zs.empty(), true);
CAF_CHECK_EQUAL(zs.size(), 0u);
CAF_CHECK_EQUAL(xs, zs);
}
CAF_TEST(iterators) {
using std::equal;
using vector_type = std::vector<int_dict::value_type>;
int_dict xs{{"a", 1}, {"b", 2}, {"c", 3}};
vector_type ys{{"a", 1}, {"b", 2}, {"c", 3}};
CAF_CHECK(equal(xs.begin(), xs.end(), ys.begin()));
CAF_CHECK(equal(xs.cbegin(), xs.cend(), ys.cbegin()));
CAF_CHECK(equal(xs.rbegin(), xs.rend(), ys.rbegin()));
CAF_CHECK(equal(xs.crbegin(), xs.crend(), ys.crbegin()));
}
CAF_TEST(swapping) {
int_dict xs{{"foo", 1}, {"bar", 2}};
int_dict ys;
int_dict zs{{"foo", 1}, {"bar", 2}};
CAF_CHECK_NOT_EQUAL(xs, ys);
CAF_CHECK_NOT_EQUAL(ys, zs);
CAF_CHECK_EQUAL(xs, zs);
xs.swap(ys);
CAF_CHECK_NOT_EQUAL(xs, ys);
CAF_CHECK_EQUAL(ys, zs);
CAF_CHECK_NOT_EQUAL(xs, zs);
}
CAF_TEST(insertion) {
int_dict xs;
CAF_CHECK_EQUAL(xs.insert("a", 1).second, true);
CAF_CHECK_EQUAL(xs.insert("b", 2).second, true);
CAF_CHECK_EQUAL(xs.insert("c", 3).second, true);
CAF_CHECK_EQUAL(xs.insert("c", 4).second, false);
int_dict ys;
CAF_CHECK_EQUAL(ys.insert_or_assign("a", 1).second, true);
CAF_CHECK_EQUAL(ys.insert_or_assign("b", 2).second, true);
CAF_CHECK_EQUAL(ys.insert_or_assign("c", 0).second, true);
CAF_CHECK_EQUAL(ys.insert_or_assign("c", 3).second, false);
CAF_CHECK_EQUAL(xs, ys);
}
CAF_TEST(insertion with hint) {
int_dict xs;
auto xs_last = xs.end();
auto xs_insert = [&](string_view key, int val) {
xs_last = xs.insert(xs_last, key, val);
};
xs_insert("a", 1);
xs_insert("c", 3);
xs_insert("b", 2);
xs_insert("c", 4);
int_dict ys;
auto ys_last = ys.end();
auto ys_insert_or_assign = [&](string_view key, int val) {
ys_last = ys.insert_or_assign(ys_last, key, val);
};
ys_insert_or_assign("a", 1);
ys_insert_or_assign("c", 0);
ys_insert_or_assign("b", 2);
ys_insert_or_assign("c", 3);
CAF_CHECK_EQUAL(xs, ys);
}
CAF_TEST(bounds) {
int_dict xs{{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}};
const int_dict& const_xs = xs;
CAF_CHECK_EQUAL(xs.lower_bound("c")->first, "c");
CAF_CHECK_EQUAL(xs.upper_bound("c")->first, "d");
CAF_CHECK_EQUAL(const_xs.lower_bound("c")->first, "c");
CAF_CHECK_EQUAL(const_xs.upper_bound("c")->first, "d");
}
CAF_TEST(find) {
int_dict xs{{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}};
const int_dict& const_xs = xs;
CAF_CHECK_EQUAL(xs.find("e"), xs.end());
CAF_CHECK_EQUAL(xs.find("a")->second, 1);
CAF_CHECK_EQUAL(xs.find("c")->second, 3);
CAF_CHECK_EQUAL(const_xs.find("e"), xs.end());
CAF_CHECK_EQUAL(const_xs.find("a")->second, 1);
CAF_CHECK_EQUAL(const_xs.find("c")->second, 3);
}
CAF_TEST(element access) {
int_dict xs{{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}};
CAF_CHECK_EQUAL(xs["a"], 1);
CAF_CHECK_EQUAL(xs["b"], 2);
CAF_CHECK_EQUAL(xs["e"], 0);
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE string_view
#include "caf/test/dsl.hpp"
#include "caf/string_view.hpp"
using namespace caf;
namespace {
struct fixture {
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(string_view_tests, fixture)
CAF_TEST(default construction) {
string_view x;
string_view y;
CAF_CHECK(x.empty());
CAF_CHECK_EQUAL(x.size(), 0u);
CAF_CHECK_EQUAL(x.data(), nullptr);
CAF_CHECK_EQUAL(y, y);
}
CAF_TEST(cstring conversion) {
string_view x = "abc";
CAF_CHECK_EQUAL(x.size(), 3u);
CAF_CHECK_EQUAL(x[0], 'a');
CAF_CHECK_EQUAL(x[1], 'b');
CAF_CHECK_EQUAL(x[2], 'c');
CAF_CHECK_EQUAL(x, "abc");
x = "def";
CAF_CHECK_NOT_EQUAL(x, "abc");
CAF_CHECK_EQUAL(x, "def");
}
CAF_TEST(string conversion) {
std::string x = "abc";
string_view y;
y = x;
CAF_CHECK_EQUAL(x, y);
auto f = [&](string_view z) {
CAF_CHECK_EQUAL(x, z);
};
f(x);
}
CAF_TEST(substrings) {
string_view x = "abcdefghi";
CAF_CHECK(x.remove_prefix(3), "defghi");
CAF_CHECK(x.remove_suffix(3), "abcdef");
CAF_CHECK(x.substr(3, 3), "def");
CAF_CHECK(x.remove_prefix(9), "");
CAF_CHECK(x.remove_suffix(9), "");
CAF_CHECK(x.substr(9), "");
CAF_CHECK(x.substr(0, 0), "");
}
CAF_TEST(compare) {
// testees
string_view x = "abc";
string_view y = "bcd";
string_view z = "cde";
// x.compare full strings
CAF_CHECK(x.compare("abc") == 0);
CAF_CHECK(x.compare(y) < 0);
CAF_CHECK(x.compare(z) < 0);
// y.compare full strings
CAF_CHECK(y.compare(x) > 0);
CAF_CHECK(y.compare("bcd") == 0);
CAF_CHECK(y.compare(z) < 0);
// z.compare full strings
CAF_CHECK(z.compare(x) > 0);
CAF_CHECK(z.compare(y) > 0);
CAF_CHECK(z.compare("cde") == 0);
// x.compare substrings
CAF_CHECK(x.compare(0, 3, "abc") == 0);
CAF_CHECK(x.compare(1, 2, y, 0, 2) == 0);
CAF_CHECK(x.compare(2, 1, z, 0, 1) == 0);
CAF_CHECK(x.compare(2, 1, z, 0, 2) == 0);
}
CAF_TEST(copy) {
char buf[10];
string_view str = "hello";
auto n = str.copy(buf, str.size());
CAF_CHECK_EQUAL(n, 5u);
buf[n] = '\0';
CAF_CHECK_EQUAL(str, string_view(buf, n));
CAF_CHECK(strcmp("hello", buf) == 0);
n = str.copy(buf, 10, 3);
buf[n] = '\0';
CAF_CHECK_EQUAL(string_view(buf, n), "lo");
CAF_CHECK(strcmp("lo", buf) == 0);
}
CAF_TEST(find) {
// Check whether string_view behaves exactly like std::string.
string_view x = "abcdef";
std::string y = "abcdef";
CAF_CHECK_EQUAL(x.find('a'), y.find('a'));
CAF_CHECK_EQUAL(x.find('b'), y.find('b'));
CAF_CHECK_EQUAL(x.find('g'), y.find('g'));
CAF_CHECK_EQUAL(x.find('a', 1), y.find('a', 1));
CAF_CHECK_EQUAL(x.find("a"), y.find("a"));
CAF_CHECK_EQUAL(x.find("bc"), y.find("bc"));
CAF_CHECK_EQUAL(x.find("ce"), y.find("ce"));
CAF_CHECK_EQUAL(x.find("bc", 1), y.find("bc", 1));
CAF_CHECK_EQUAL(x.find("bc", 1, 0), y.find("bc", 1, 0));
CAF_CHECK_EQUAL(x.find("bc", 0, 1), y.find("bc", 0, 1));
CAF_CHECK_EQUAL(x.find("bc", 2, 10), y.find("bc", 2, 10));
}
CAF_TEST(rfind) {
// Check whether string_view behaves exactly like std::string.
string_view x = "abccba";
std::string y = "abccba";
CAF_CHECK_EQUAL(x.rfind('a'), y.rfind('a'));
CAF_CHECK_EQUAL(x.rfind('b'), y.rfind('b'));
CAF_CHECK_EQUAL(x.rfind('g'), y.rfind('g'));
CAF_CHECK_EQUAL(x.rfind('a', 1), y.rfind('a', 1));
CAF_CHECK_EQUAL(x.rfind("a"), y.rfind("a"));
CAF_CHECK_EQUAL(x.rfind("bc"), y.rfind("bc"));
CAF_CHECK_EQUAL(x.rfind("ce"), y.rfind("ce"));
CAF_CHECK_EQUAL(x.rfind("bc", 1), y.rfind("bc", 1));
CAF_CHECK_EQUAL(x.rfind("bc", 1, 0), y.rfind("bc", 1, 0));
CAF_CHECK_EQUAL(x.rfind("bc", 0, 1), y.rfind("bc", 0, 1));
CAF_CHECK_EQUAL(x.rfind("bc", 2, 10), y.rfind("bc", 2, 10));
}
CAF_TEST(find_first_of) {
// Check whether string_view behaves exactly like std::string.
string_view x = "abcdef";
std::string y = "abcdef";
CAF_CHECK_EQUAL(x.find_first_of('a'), y.find_first_of('a'));
CAF_CHECK_EQUAL(x.find_first_of('b'), y.find_first_of('b'));
CAF_CHECK_EQUAL(x.find_first_of('g'), y.find_first_of('g'));
CAF_CHECK_EQUAL(x.find_first_of('a', 1), y.find_first_of('a', 1));
CAF_CHECK_EQUAL(x.find_first_of("a"), y.find_first_of("a"));
CAF_CHECK_EQUAL(x.find_first_of("bc"), y.find_first_of("bc"));
CAF_CHECK_EQUAL(x.find_first_of("ce"), y.find_first_of("ce"));
CAF_CHECK_EQUAL(x.find_first_of("bc", 1), y.find_first_of("bc", 1));
CAF_CHECK_EQUAL(x.find_first_of("bc", 1, 0), y.find_first_of("bc", 1, 0));
CAF_CHECK_EQUAL(x.find_first_of("bc", 0, 1), y.find_first_of("bc", 0, 1));
CAF_CHECK_EQUAL(x.find_first_of("bc", 2, 10), y.find_first_of("bc", 2, 10));
}
CAF_TEST(find_last_of) {
// Check whether string_view behaves exactly like std::string.
string_view x = "abcdef";
std::string y = "abcdef";
CAF_CHECK_EQUAL(x.find_last_of('a'), y.find_last_of('a'));
CAF_CHECK_EQUAL(x.find_last_of('b'), y.find_last_of('b'));
CAF_CHECK_EQUAL(x.find_last_of('g'), y.find_last_of('g'));
CAF_CHECK_EQUAL(x.find_last_of('a', 1), y.find_last_of('a', 1));
CAF_CHECK_EQUAL(x.find_last_of("a"), y.find_last_of("a"));
CAF_CHECK_EQUAL(x.find_last_of("bc"), y.find_last_of("bc"));
CAF_CHECK_EQUAL(x.find_last_of("ce"), y.find_last_of("ce"));
CAF_CHECK_EQUAL(x.find_last_of("bc", 1), y.find_last_of("bc", 1));
CAF_CHECK_EQUAL(x.find_last_of("bc", 1, 0), y.find_last_of("bc", 1, 0));
CAF_CHECK_EQUAL(x.find_last_of("bc", 0, 1), y.find_last_of("bc", 0, 1));
CAF_CHECK_EQUAL(x.find_last_of("bc", 2, 10), y.find_last_of("bc", 2, 10));
}
CAF_TEST(find_first_not_of) {
// Check whether string_view behaves exactly like std::string.
string_view x = "abcdef";
std::string y = "abcdef";
CAF_CHECK_EQUAL(x.find_first_not_of('a'), y.find_first_not_of('a'));
CAF_CHECK_EQUAL(x.find_first_not_of('b'), y.find_first_not_of('b'));
CAF_CHECK_EQUAL(x.find_first_not_of('g'), y.find_first_not_of('g'));
CAF_CHECK_EQUAL(x.find_first_not_of('a', 1), y.find_first_not_of('a', 1));
CAF_CHECK_EQUAL(x.find_first_not_of("a"), y.find_first_not_of("a"));
CAF_CHECK_EQUAL(x.find_first_not_of("bc"), y.find_first_not_of("bc"));
CAF_CHECK_EQUAL(x.find_first_not_of("ce"), y.find_first_not_of("ce"));
CAF_CHECK_EQUAL(x.find_first_not_of("bc", 1), y.find_first_not_of("bc", 1));
CAF_CHECK_EQUAL(x.find_first_not_of("bc", 1, 0),
y.find_first_not_of("bc", 1, 0));
CAF_CHECK_EQUAL(x.find_first_not_of("bc", 0, 1),
y.find_first_not_of("bc", 0, 1));
CAF_CHECK_EQUAL(x.find_first_not_of("bc", 2, 10),
y.find_first_not_of("bc", 2, 10));
}
CAF_TEST(find_last_not_of) {
// Check whether string_view behaves exactly like std::string.
string_view x = "abcdef";
std::string y = "abcdef";
CAF_CHECK_EQUAL(x.find_last_not_of('a'), y.find_last_not_of('a'));
CAF_CHECK_EQUAL(x.find_last_not_of('b'), y.find_last_not_of('b'));
CAF_CHECK_EQUAL(x.find_last_not_of('g'), y.find_last_not_of('g'));
CAF_CHECK_EQUAL(x.find_last_not_of('a', 1), y.find_last_not_of('a', 1));
CAF_CHECK_EQUAL(x.find_last_not_of("a"), y.find_last_not_of("a"));
CAF_CHECK_EQUAL(x.find_last_not_of("bc"), y.find_last_not_of("bc"));
CAF_CHECK_EQUAL(x.find_last_not_of("ce"), y.find_last_not_of("ce"));
CAF_CHECK_EQUAL(x.find_last_not_of("bc", 1), y.find_last_not_of("bc", 1));
CAF_CHECK_EQUAL(x.find_last_not_of("bc", 1, 0),
y.find_last_not_of("bc", 1, 0));
CAF_CHECK_EQUAL(x.find_last_not_of("bc", 0, 1),
y.find_last_not_of("bc", 0, 1));
CAF_CHECK_EQUAL(x.find_last_not_of("bc", 2, 10),
y.find_last_not_of("bc", 2, 10));
}
CAF_TEST_FIXTURE_SCOPE_END()
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