Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
04ede6c4
Commit
04ede6c4
authored
Jun 05, 2018
by
Dominik Charousset
Committed by
Dominik Charousset
Jun 07, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add automagic unboxing for homogeneous lists/maps
parent
dc0f0375
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
368 additions
and
14 deletions
+368
-14
libcaf_core/caf/config_value.hpp
libcaf_core/caf/config_value.hpp
+298
-0
libcaf_core/test/config_value.cpp
libcaf_core/test/config_value.cpp
+70
-14
No files found.
libcaf_core/caf/config_value.hpp
View file @
04ede6c4
...
@@ -29,6 +29,7 @@
...
@@ -29,6 +29,7 @@
#include "caf/default_sum_type_access.hpp"
#include "caf/default_sum_type_access.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp"
#include "caf/fwd.hpp"
#include "caf/optional.hpp"
#include "caf/sum_type.hpp"
#include "caf/sum_type.hpp"
#include "caf/sum_type_access.hpp"
#include "caf/sum_type_access.hpp"
#include "caf/timestamp.hpp"
#include "caf/timestamp.hpp"
...
@@ -163,11 +164,308 @@ private:
...
@@ -163,11 +164,308 @@ private:
variant_type
data_
;
variant_type
data_
;
};
};
/*
/// Enable `holds_alternative`, `get`, `get_if`, and `visit` for `config_value`.
/// Enable `holds_alternative`, `get`, `get_if`, and `visit` for `config_value`.
/// @relates config_value
/// @relates config_value
/// @relates SumType
/// @relates SumType
///
template <>
template <>
struct sum_type_access<config_value> : default_sum_type_access<config_value> {};
struct sum_type_access<config_value> : default_sum_type_access<config_value> {};
*/
template
<
class
T
>
struct
config_value_access
;
template
<
class
T
>
bool
holds_alternative
(
const
config_value
&
x
)
{
return
config_value_access
<
T
>::
is
(
x
);
}
template
<
class
T
>
auto
get_if
(
const
config_value
*
x
)
->
decltype
(
config_value_access
<
T
>::
get_if
(
x
))
{
return
config_value_access
<
T
>::
get_if
(
x
);
}
template
<
class
T
>
auto
get
(
const
config_value
&
x
)
->
decltype
(
config_value_access
<
T
>::
get
(
x
))
{
return
config_value_access
<
T
>::
get
(
x
);
}
template
<
class
Visitor
>
auto
visit
(
Visitor
&&
f
,
config_value
&
x
)
->
decltype
(
visit
(
std
::
forward
<
Visitor
>
(
f
),
x
.
get_data
()))
{
return
visit
(
std
::
forward
<
Visitor
>
(
f
),
x
.
get_data
());
}
template
<
class
Visitor
>
auto
visit
(
Visitor
&&
f
,
const
config_value
&
x
)
->
decltype
(
visit
(
std
::
forward
<
Visitor
>
(
f
),
x
.
get_data
()))
{
return
visit
(
std
::
forward
<
Visitor
>
(
f
),
x
.
get_data
());
}
template
<
class
T
>
struct
default_config_value_access
{
static
bool
is
(
const
config_value
&
x
)
{
return
holds_alternative
<
T
>
(
x
.
get_data
());
}
static
const
T
*
get_if
(
const
config_value
*
x
)
{
return
caf
::
get_if
<
T
>
(
&
(
x
->
get_data
()));
}
static
const
T
&
get
(
const
config_value
&
x
)
{
return
caf
::
get
<
T
>
(
x
.
get_data
());
}
};
#define CAF_CONFIG_VALUE_DEFAULT_ACCESS(subtype) \
template <> \
struct config_value_access<config_value::subtype> \
: default_config_value_access<config_value::subtype> {}
CAF_CONFIG_VALUE_DEFAULT_ACCESS
(
integer
);
CAF_CONFIG_VALUE_DEFAULT_ACCESS
(
boolean
);
CAF_CONFIG_VALUE_DEFAULT_ACCESS
(
real
);
CAF_CONFIG_VALUE_DEFAULT_ACCESS
(
atom
);
CAF_CONFIG_VALUE_DEFAULT_ACCESS
(
timespan
);
CAF_CONFIG_VALUE_DEFAULT_ACCESS
(
string
);
CAF_CONFIG_VALUE_DEFAULT_ACCESS
(
list
);
CAF_CONFIG_VALUE_DEFAULT_ACCESS
(
dictionary
);
/// Implements automagic unboxing of `std::vector<T>` from a homogeneous
/// `config_value::list`.
/// @relates config_value
template
<
class
T
>
struct
config_value_access
<
std
::
vector
<
T
>>
{
using
vector_type
=
std
::
vector
<
T
>
;
static
bool
is
(
const
config_value
&
x
)
{
auto
lst
=
caf
::
get_if
<
config_value
::
list
>
(
&
x
);
if
(
lst
!=
nullptr
)
{
return
std
::
all_of
(
lst
->
begin
(),
lst
->
end
(),
[](
const
config_value
&
x
)
{
return
caf
::
holds_alternative
<
T
>
(
x
);
});
}
return
false
;
}
static
optional
<
vector_type
>
get_if
(
const
config_value
*
x
)
{
vector_type
result
;
auto
extract
=
[
&
](
const
config_value
&
y
)
{
auto
opt
=
caf
::
get_if
<
T
>
(
&
y
);
if
(
opt
)
{
result
.
emplace_back
(
*
opt
);
return
true
;
}
return
false
;
};
auto
lst
=
caf
::
get_if
<
config_value
::
list
>
(
x
);
if
(
lst
!=
nullptr
&&
std
::
all_of
(
lst
->
begin
(),
lst
->
end
(),
extract
))
return
result
;
return
none
;
}
static
vector_type
get
(
const
config_value
&
x
)
{
auto
result
=
get_if
(
&
x
);
if
(
!
result
)
CAF_RAISE_ERROR
(
"invalid type found"
);
return
std
::
move
(
*
result
);
}
};
/// Implements automagic unboxing of `std::map<std::string, 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
>
;
using
kvp
=
std
::
pair
<
const
std
::
string
,
config_value
>
;
static
bool
is
(
const
config_value
&
x
)
{
auto
lst
=
caf
::
get_if
<
config_value
::
dictionary
>
(
&
x
);
if
(
lst
!=
nullptr
)
{
return
std
::
all_of
(
lst
->
begin
(),
lst
->
end
(),
[](
const
kvp
&
x
)
{
return
config_value_access
<
V
>::
is
(
x
.
second
);
});
}
return
false
;
}
static
optional
<
map_type
>
get_if
(
const
config_value
*
x
)
{
map_type
result
;
auto
extract
=
[
&
](
const
kvp
&
y
)
{
auto
opt
=
caf
::
get_if
<
V
>
(
&
(
y
.
second
));
if
(
opt
)
{
result
.
emplace
(
y
.
first
,
std
::
move
(
*
opt
));
return
true
;
}
return
false
;
};
auto
lst
=
caf
::
get_if
<
config_value
::
dictionary
>
(
x
);
if
(
lst
!=
nullptr
&&
std
::
all_of
(
lst
->
begin
(),
lst
->
end
(),
extract
))
return
result
;
return
none
;
}
static
map_type
get
(
const
config_value
&
x
)
{
auto
result
=
get_if
(
&
x
);
if
(
!
result
)
CAF_RAISE_ERROR
(
"invalid type found"
);
return
std
::
move
(
*
result
);
}
};
/// Retrieves.
/// @relates config_value
template
<
class
T
>
optional
<
T
>
get_if
(
const
config_value
::
dictionary
*
xs
,
std
::
initializer_list
<
std
::
string
>
path
)
{
// Sanity check.
if
(
path
.
size
()
==
0
)
return
none
;
// Resolve path, i.e., find the requested submap.
auto
current
=
xs
;
auto
leaf
=
path
.
end
()
-
1
;
for
(
auto
i
=
path
.
begin
();
i
!=
leaf
;
++
i
)
{
auto
j
=
current
->
find
(
*
i
);
if
(
j
==
current
->
end
())
return
none
;
current
=
caf
::
get_if
<
config_value
::
dictionary
>
(
&
j
->
second
);
if
(
current
==
nullptr
)
return
none
;
}
// Get the leaf value.
auto
j
=
current
->
find
(
*
leaf
);
if
(
j
==
current
->
end
())
return
none
;
auto
result
=
caf
::
get_if
<
T
>
(
&
j
->
second
);
if
(
result
)
return
*
result
;
return
none
;
}
/// @relates config_value
template
<
class
T
>
optional
<
T
>
get_if
(
const
config_value
::
dictionary
*
xs
,
std
::
string
path
)
{
return
get_if
<
T
>
(
xs
,
{
path
});
}
/// @relates config_value
template
<
class
T
>
T
get
(
const
config_value
::
dictionary
&
xs
,
std
::
initializer_list
<
std
::
string
>
path
)
{
auto
result
=
get_if
<
T
>
(
&
xs
,
std
::
move
(
path
));
if
(
!
result
)
CAF_RAISE_ERROR
(
"invalid type found"
);
return
std
::
move
(
*
result
);
}
/// @relates config_value
template
<
class
T
>
T
get
(
const
config_value
::
dictionary
&
xs
,
std
::
string
path
)
{
return
get
<
T
>
(
xs
,
{
path
});
}
/// @relates config_value
template
<
class
T
>
T
get_or
(
const
config_value
::
dictionary
&
xs
,
std
::
initializer_list
<
std
::
string
>
path
,
const
T
&
default_value
)
{
auto
result
=
get
<
T
>
(
xs
,
std
::
move
(
path
));
if
(
result
)
return
*
result
;
return
default_value
;
}
/// @relates config_value
template
<
class
T
>
T
get_or
(
const
config_value
::
dictionary
&
xs
,
std
::
string
path
,
const
T
&
default_value
)
{
return
get_or
(
xs
,
{
path
},
default_value
);
}
/*
template <class T>
struct config_value_access<std::vector<T>> {
using vector_type = std::vector<T>;
static bool is(const config_value& x) {
auto lst = caf::get_if<config_value::list>(&x);
if (lst != nullptr) {
return std::all_of(lst->begin(), lst->end(), [](const config_value& x) {
return caf::holds_alternative<T>(x);
});
}
return false;
}
static optional<vector_type> get_if(const config_value* x) {
vector_type result;
auto extract = [&](const config_value& y) {
auto opt = caf::get_if<T>(&y);
if (opt) {
result.emplace_back(*opt);
return true;
}
return false;
};
auto lst = caf::get_if<config_value::list>(x);
if (lst != nullptr && std::all_of(lst->begin(), lst->end(), extract))
return result;
return none;
}
static vector_type get(const config_value& x) {
auto result = get_if(x);
if (!result)
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
};
template <class V>
struct config_value_access<std::map<std::string, V>> {
using map_type = std::map<std::string, V>;
using kvp = std::pair<const std::string, config_value>;
static bool is(const config_value& x) {
auto lst = caf::get_if<config_value::dictionary>(&x);
if (lst != nullptr) {
return std::all_of(lst->begin(), lst->end(), [](const kvp& x) {
return config_value_access<V>::is(x.second);
});
}
return false;
}
static optional<map_type> get_if(const config_value* x) {
map_type result;
auto extract = [&](const kvp& y) {
auto opt = caf::get_if<V>(y.second);
if (opt) {
result.emplace_back(y.first, *opt);
return true;
}
return false;
};
auto lst = caf::get_if<config_value::dictionary>(x);
if (lst != nullptr && std::all_of(lst->begin(), lst->end(), extract))
return result;
return none;
}
static map_type get(const config_value& x) {
auto result = get_if(x);
if (!result)
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
};
*/
/// @relates config_value
/// @relates config_value
bool
operator
<
(
const
config_value
&
x
,
const
config_value
&
y
);
bool
operator
<
(
const
config_value
&
x
,
const
config_value
&
y
);
...
...
libcaf_core/test/config_value.cpp
View file @
04ede6c4
...
@@ -39,6 +39,33 @@ using list = config_value::list;
...
@@ -39,6 +39,33 @@ using list = config_value::list;
using
dictionary
=
config_value
::
dictionary
;
using
dictionary
=
config_value
::
dictionary
;
struct
dictionary_builder
{
dictionary
dict
;
dictionary_builder
&&
add
(
const
char
*
key
,
config_value
value
)
&&
{
dict
.
emplace
(
key
,
std
::
move
(
value
));
return
std
::
move
(
*
this
);
}
dictionary
make
()
&&
{
return
std
::
move
(
dict
);
}
config_value
make_cv
()
&&
{
return
config_value
{
std
::
move
(
dict
)};
}
};
dictionary_builder
dict
()
{
return
{};
}
template
<
class
...
Ts
>
config_value
cfg_lst
(
Ts
&&
...
xs
)
{
config_value
::
list
lst
{
config_value
{
std
::
forward
<
Ts
>
(
xs
)}...};
return
config_value
{
std
::
move
(
lst
)};
}
}
// namespace <anonymous>
}
// namespace <anonymous>
CAF_TEST
(
default_constructed
)
{
CAF_TEST
(
default_constructed
)
{
...
@@ -49,9 +76,13 @@ CAF_TEST(default_constructed) {
...
@@ -49,9 +76,13 @@ CAF_TEST(default_constructed) {
}
}
CAF_TEST
(
list
)
{
CAF_TEST
(
list
)
{
auto
x
=
make_config_value_list
(
1
,
2
,
3
);
using
integer_list
=
std
::
vector
<
int64_t
>
;
CAF_CHECK_EQUAL
(
to_string
(
x
),
"[1, 2, 3]"
);
auto
xs
=
make_config_value_list
(
1
,
2
,
3
);
CAF_CHECK_EQUAL
(
x
.
type_name
(),
config_value
::
type_name_of
<
list
>
());
CAF_CHECK_EQUAL
(
to_string
(
xs
),
"[1, 2, 3]"
);
CAF_CHECK_EQUAL
(
xs
.
type_name
(),
config_value
::
type_name_of
<
list
>
());
CAF_CHECK_EQUAL
(
holds_alternative
<
config_value
::
list
>
(
xs
),
true
);
CAF_CHECK_EQUAL
(
holds_alternative
<
integer_list
>
(
xs
),
true
);
CAF_CHECK_EQUAL
(
get
<
integer_list
>
(
xs
),
integer_list
({
1
,
2
,
3
}));
}
}
CAF_TEST
(
convert_to_list
)
{
CAF_TEST
(
convert_to_list
)
{
...
@@ -74,15 +105,40 @@ CAF_TEST(append) {
...
@@ -74,15 +105,40 @@ CAF_TEST(append) {
CAF_CHECK_EQUAL
(
to_string
(
x
),
"[1, 2, 'foo']"
);
CAF_CHECK_EQUAL
(
to_string
(
x
),
"[1, 2, 'foo']"
);
}
}
CAF_TEST
(
maps
)
{
CAF_TEST
(
homogeneous
dictionary
)
{
dictionary
xs
;
using
integer_map
=
std
::
map
<
std
::
string
,
int64_t
>
;
xs
[
"num"
]
=
int64_t
{
42
};
auto
xs
=
dict
()
xs
[
"atm"
]
=
atom
(
"hello"
);
.
add
(
"value-1"
,
config_value
{
1
})
xs
[
"str"
]
=
string
{
"foobar"
};
.
add
(
"value-2"
,
config_value
{
2
})
xs
[
"dur"
]
=
timespan
{
100
};
.
add
(
"value-3"
,
config_value
{
3
})
config_value
x
{
xs
};
.
add
(
"value-4"
,
config_value
{
4
})
CAF_CHECK_EQUAL
(
x
.
type_name
(),
config_value
::
type_name_of
<
dictionary
>
());
.
make
();
CAF_CHECK_EQUAL
(
integer_map
ys
{
to_string
(
x
),
{
"value-1"
,
1
},
R"([("atm", 'hello'), ("dur", 100ns), ("num", 42), ("str", "foobar")])"
);
{
"value-2"
,
2
},
{
"value-3"
,
3
},
{
"value-4"
,
4
},
};
CAF_CHECK_EQUAL
(
get
<
int64_t
>
(
xs
,
"value-1"
),
1
);
CAF_CHECK_EQUAL
(
get
<
integer_map
>
(
config_value
{
xs
}),
ys
);
}
CAF_TEST
(
heterogeneous
dictionary
)
{
using
string_list
=
std
::
vector
<
std
::
string
>
;
auto
xs
=
dict
()
.
add
(
"scheduler"
,
dict
()
.
add
(
"policy"
,
config_value
{
atom
(
"none"
)})
.
add
(
"max-threads"
,
config_value
{
2
})
.
make_cv
())
.
add
(
"nodes"
,
dict
()
.
add
(
"preload"
,
cfg_lst
(
"sun"
,
"venus"
,
"mercury"
,
"earth"
,
"mars"
))
.
make_cv
())
.
make
();
CAF_CHECK_EQUAL
(
get
<
atom_value
>
(
xs
,
{
"scheduler"
,
"policy"
}),
atom
(
"none"
));
CAF_CHECK_EQUAL
(
get
<
int64_t
>
(
xs
,
{
"scheduler"
,
"max-threads"
}),
2
);
CAF_CHECK_EQUAL
(
get_if
<
double
>
(
&
xs
,
{
"scheduler"
,
"max-threads"
}),
none
);
string_list
nodes
{
"sun"
,
"venus"
,
"mercury"
,
"earth"
,
"mars"
};
CAF_CHECK_EQUAL
(
get
<
string_list
>
(
xs
,
{
"nodes"
,
"preload"
}),
nodes
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment