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
42d39ac9
Commit
42d39ac9
authored
Oct 09, 2015
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve documentation and unit tests of INI parser
parent
decfb968
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
137 additions
and
62 deletions
+137
-62
libcaf_core/caf/detail/parse_ini.hpp
libcaf_core/caf/detail/parse_ini.hpp
+3
-4
libcaf_core/caf/parse_config.hpp
libcaf_core/caf/parse_config.hpp
+5
-4
libcaf_core/src/parse_config.cpp
libcaf_core/src/parse_config.cpp
+1
-1
libcaf_core/src/parse_ini.cpp
libcaf_core/src/parse_ini.cpp
+31
-18
libcaf_core/test/parse_ini.cpp
libcaf_core/test/parse_ini.cpp
+97
-35
No files found.
libcaf_core/caf/detail/parse_ini.hpp
View file @
42d39ac9
...
...
@@ -25,14 +25,13 @@
#include "caf/parse_config.hpp"
namespace
caf
{
namespace
detail
{
/// Parse the given input stream as INI formatted data and calls the consumer
/// with every key-value pair.
/// @param raw_data
the INI formatted input stream
/// @param errors
a stream of all errors which occure while parsing
/// @param consumer
a function that consums the key-value pairs
/// @param raw_data
Input stream of INI formatted text.
/// @param errors
Output stream for parser errors.
/// @param consumer
Callback consuming generated key-value pairs.
void
parse_ini
(
std
::
istream
&
raw_data
,
std
::
ostream
&
errors
,
config_consumer
consumer
);
...
...
libcaf_core/caf/parse_config.hpp
View file @
42d39ac9
...
...
@@ -20,25 +20,26 @@
#ifndef CAF_PARSER_CONFIG_HPP
#define CAF_PARSER_CONFIG_HPP
#include <algorithm>
#include <string>
#include <algorithm>
#include "caf/variant.hpp"
namespace
caf
{
/// Denotes the format of a configuration file.
enum
class
config_format
{
auto_detect
,
ini
};
/// Denotes a configuration value.
using
config_value
=
variant
<
std
::
string
,
double
,
int64_t
,
bool
>
;
/// Denotes a callback for config parser implementations.
using
config_consumer
=
std
::
function
<
void
(
std
::
string
,
config_value
)
>
;
/// parse_config
/// @param file_name
/// @param cf
/// Parse `file_name` using given file format `cf`.
void
parse_config
(
const
std
::
string
&
file_name
,
config_format
cf
=
config_format
::
auto_detect
);
...
...
libcaf_core/src/parse_config.cpp
View file @
42d39ac9
...
...
@@ -58,7 +58,7 @@ void parse_config(const std::string& file_name,
consumer
(
const
consumer
&
)
=
default
;
consumer
&
operator
=
(
const
consumer
&
)
=
default
;
void
operator
()(
std
::
string
key
,
config_value
value
)
const
{
void
operator
()(
const
std
::
string
&
,
config_
value
)
const
{
// send message to config server
}
};
...
...
libcaf_core/src/parse_ini.cpp
View file @
42d39ac9
...
...
@@ -19,10 +19,10 @@
#include "caf/detail/parse_ini.hpp"
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include "caf/string_algorithms.hpp"
...
...
@@ -43,12 +43,11 @@ void detail::parse_ini(std::istream& raw_data, std::ostream& errors,
continue
;
// do we read a group name?
if
(
*
bol
==
'['
)
{
if
(
*
(
eol
-
1
)
!=
']'
)
{
if
(
*
(
eol
-
1
)
!=
']'
)
errors
<<
"error in line "
<<
ln
<<
": missing ] at end of line"
<<
std
::
endl
;
}
else
{
else
group
.
assign
(
bol
+
1
,
eol
-
1
);
}
// skip further processing of this line
continue
;
}
...
...
@@ -78,28 +77,43 @@ void detail::parse_ini(std::istream& raw_data, std::ostream& errors,
key
+=
'.'
;
// ignore any whitespace between config-name and equal sign
key
.
insert
(
key
.
end
(),
bol
,
find_if
(
bol
,
eqs
,
::
isspace
));
// begin-of-value, ignor
e
ing whitespaces after '='
// begin-of-value, ignoring whitespaces after '='
auto
bov
=
find_if_not
(
eqs
+
1
,
eol
,
::
isspace
);
// auto-detect what we are dealing with
const
char
*
true_str
=
"true"
;
const
char
*
false_str
=
"false"
;
if
(
std
::
equal
(
bov
,
eol
,
true_str
))
{
constexpr
const
char
*
true_str
=
"true"
;
constexpr
const
char
*
false_str
=
"false"
;
auto
icase_eq
=
[](
char
x
,
char
y
)
{
return
tolower
(
x
)
==
tolower
(
y
);
};
if
(
std
::
equal
(
bov
,
eol
,
true_str
,
icase_eq
))
{
consumer
(
std
::
move
(
key
),
true
);
}
else
if
(
std
::
equal
(
bov
,
eol
,
false_str
))
{
}
else
if
(
std
::
equal
(
bov
,
eol
,
false_str
,
icase_eq
))
{
consumer
(
std
::
move
(
key
),
false
);
}
else
if
(
*
bov
==
'"'
)
{
// found a string, remove first and last char from string, start escaping
// string sequence
// end-of-string iterator
auto
eos
=
eol
-
1
;
if
(
bov
==
eos
)
{
errors
<<
"error in line "
<<
ln
<<
": stray '
\"
'"
<<
std
::
endl
;
continue
;
}
if
(
*
eos
!=
'"'
)
{
errors
<<
"error in line "
<<
ln
<<
": string not terminated by '
\"
'"
<<
std
::
endl
;
continue
;
}
// found a string, remove first and last char from string,
// start escaping string sequence
auto
last_char_backslash
=
false
;
std
::
string
result
=
""
;
std
::
string
result
;
// skip leading " and iterate up to the trailing "
++
bov
;
for
(;
bov
+
1
!=
eol
;
++
bov
)
{
for
(;
bov
!=
eos
;
++
bov
)
{
if
(
last_char_backslash
)
{
switch
(
*
bov
)
{
case
'n'
:
result
+=
'\n'
;
break
;
case
't'
:
result
+=
'\t'
;
break
;
default:
result
+=
*
bov
;
}
...
...
@@ -111,8 +125,8 @@ void detail::parse_ini(std::istream& raw_data, std::ostream& errors,
}
}
if
(
last_char_backslash
)
{
errors
<<
"
error
in line "
<<
ln
<<
": trailing quotation mark
was
escaped"
<<
std
::
endl
;
errors
<<
"
warning
in line "
<<
ln
<<
": trailing quotation mark escaped"
<<
std
::
endl
;
}
consumer
(
std
::
move
(
key
),
std
::
move
(
result
));
}
else
{
...
...
@@ -158,8 +172,7 @@ void detail::parse_ini(std::istream& raw_data, std::ostream& errors,
if
(
e
==
&*
eol
)
{
consumer
(
std
::
move
(
key
),
is_neg
?
-
res
:
res
);
}
else
{
errors
<<
"error in line "
<<
ln
<<
": can't parse value to double"
<<
std
::
endl
;
errors
<<
"error in line "
<<
ln
<<
": invalid value"
<<
std
::
endl
;
}
}
}
...
...
libcaf_core/test/parse_ini.cpp
View file @
42d39ac9
...
...
@@ -19,25 +19,21 @@
#include "caf/config.hpp"
#define CAF_SUITE
ini_parser
#define CAF_SUITE
parse_ini
#include "caf/test/unit_test.hpp"
#include <iostream>
#include <sstream>
#include "caf/all.hpp"
#include "caf/detail/parse_ini.hpp"
#include "caf/detail/safe_equal.hpp"
using
namespace
caf
;
namespace
{
template
<
class
T
>
bool
value_is
(
const
config_value
&
cv
,
const
T
&
what
)
{
const
T
*
ptr
=
get
<
T
>
(
&
cv
);
return
ptr
!=
nullptr
&&
*
ptr
==
what
;
}
constexpr
const
char
*
case1
=
R"__(
[scheduler]
policy="work-sharing"
...
...
@@ -63,40 +59,106 @@ buzz=1E-34
)__"
;
constexpr
const
char
*
case3
=
R"__("
[whoops
foo="bar"
[test]
; provoke some more errors
foo bar
=42
baz=
foo="
bar="foo
some-int=42
some-string="hi there!\"
neg=-
wtf=0x3733T
hu=0779
hop=--"hiho"
)__"
;
struct
fixture
{
void
load
(
const
char
*
str
)
{
auto
f
=
[
&
](
std
::
string
key
,
config_value
value
)
{
values
.
emplace
(
std
::
move
(
key
),
std
::
move
(
value
));
};
std
::
stringstream
ss
;
std
::
stringstream
err
;
ss
<<
str
;
detail
::
parse_ini
(
ss
,
err
,
f
);
split
(
errors
,
err
.
str
(),
is_any_of
(
"
\n
"
),
token_compress_on
);
}
bool
has_error
(
const
char
*
err
)
{
return
std
::
any_of
(
errors
.
begin
(),
errors
.
end
(),
[
=
](
const
std
::
string
&
str
)
{
return
str
==
err
;
});
}
template
<
class
T
>
bool
value_is
(
const
char
*
key
,
const
T
&
what
)
{
auto
&
cv
=
values
[
key
];
using
type
=
typename
std
::
conditional
<
std
::
is_convertible
<
T
,
std
::
string
>::
value
,
std
::
string
,
typename
std
::
conditional
<
std
::
is_integral
<
T
>::
value
&&
!
std
::
is_same
<
T
,
bool
>::
value
,
int64_t
,
T
>::
type
>::
type
;
auto
ptr
=
get
<
type
>
(
&
cv
);
return
ptr
!=
nullptr
&&
detail
::
safe_equal
(
*
ptr
,
what
);
}
std
::
map
<
std
::
string
,
config_value
>
values
;
std
::
vector
<
std
::
string
>
errors
;
};
}
// namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE
(
parse_ini_tests
,
fixture
)
CAF_TEST
(
simple_ini
)
{
std
::
map
<
std
::
string
,
config_value
>
values
;
auto
f
=
[
&
](
std
::
string
key
,
config_value
value
)
{
values
.
emplace
(
std
::
move
(
key
),
std
::
move
(
value
));
};
std
::
stringstream
ss
;
std
::
stringstream
err
;
ss
<<
case1
;
detail
::
parse_ini
(
ss
,
err
,
f
);
load
(
case1
);
CAF_CHECK
(
errors
.
empty
());
CAF_CHECK
(
values
.
count
(
"nexus.port"
)
>
0
);
CAF_CHECK
(
value_is
(
values
[
"nexus.port"
],
int64_t
{
4242
}
));
CAF_CHECK
(
value_is
(
values
[
"nexus.host"
],
std
::
string
{
"127.0.0.1"
}
));
CAF_CHECK
(
value_is
(
values
[
"scheduler.policy"
],
std
::
string
{
"work-sharing"
}
));
CAF_CHECK
(
value_is
(
values
[
"scheduler.max-threads"
],
int64_t
{
2
}
));
CAF_CHECK
(
value_is
(
values
[
"middleman.automatic-connections"
],
bool
{
true
}
));
CAF_CHECK
(
value_is
(
"nexus.port"
,
4242
));
CAF_CHECK
(
value_is
(
"nexus.host"
,
"127.0.0.1"
));
CAF_CHECK
(
value_is
(
"scheduler.policy"
,
"work-sharing"
));
CAF_CHECK
(
value_is
(
"scheduler.max-threads"
,
2
));
CAF_CHECK
(
value_is
(
"middleman.automatic-connections"
,
true
));
CAF_CHECK
(
values
.
count
(
"cash.greeting"
)
>
0
);
CAF_CHECK
(
value_is
(
values
[
"cash.greeting"
],
std
::
string
{
"Hi there, this is
\"
CASH!
\"\n
~
\\
~ use at your own risk ~
\\
~"
}));
CAF_CHECK
(
value_is
(
"cash.greeting"
,
"Hi there, this is
\"
CASH!
\"\n
~
\\
~ use at your own risk ~
\\
~"
));
}
CAF_TEST
(
numbers
)
{
std
::
map
<
std
::
string
,
config_value
>
values
;
auto
f
=
[
&
](
std
::
string
key
,
config_value
value
)
{
values
.
emplace
(
std
::
move
(
key
),
std
::
move
(
value
));
};
std
::
stringstream
ss
;
std
::
stringstream
err
;
ss
<<
case2
;
detail
::
parse_ini
(
ss
,
err
,
f
);
CAF_CHECK
(
value_is
(
values
[
"test.foo"
],
int64_t
{
-
0xff
}));
CAF_CHECK
(
value_is
(
values
[
"test.bar"
],
int64_t
{
034
}));
CAF_CHECK
(
value_is
(
values
[
"test.baz"
],
double
{
-
0.23
}));
CAF_CHECK
(
value_is
(
values
[
"test.buzz"
],
double
{
1E-34
}));
load
(
case2
);
CAF_CHECK
(
errors
.
empty
());
CAF_CHECK
(
value_is
(
"test.foo"
,
-
0xff
));
CAF_CHECK
(
value_is
(
"test.bar"
,
034
));
CAF_CHECK
(
value_is
(
"test.baz"
,
-
0.23
));
CAF_CHECK
(
value_is
(
"test.buzz"
,
1E-34
));
}
CAF_TEST
(
errors
)
{
load
(
case3
);
CAF_CHECK
(
has_error
(
"error in line 2: missing ] at end of line"
));
CAF_CHECK
(
has_error
(
"error in line 3: value outside of a group"
));
CAF_CHECK
(
has_error
(
"error in line 6: no '=' found"
));
CAF_CHECK
(
has_error
(
"error in line 7: line starting with '='"
));
CAF_CHECK
(
has_error
(
"error in line 8: line ends with '='"
));
CAF_CHECK
(
has_error
(
"error in line 9: stray '
\"
'"
));
CAF_CHECK
(
has_error
(
"error in line 10: string not terminated by '
\"
'"
));
CAF_CHECK
(
has_error
(
"warning in line 12: trailing quotation mark escaped"
));
CAF_CHECK
(
has_error
(
"error in line 13: '-' is not a number"
));
CAF_CHECK
(
has_error
(
"error in line 14: invalid hex value"
));
CAF_CHECK
(
has_error
(
"error in line 15: invalid oct value"
));
CAF_CHECK
(
has_error
(
"error in line 16: invalid value"
));
CAF_CHECK
(
values
.
size
()
==
2
);
CAF_CHECK
(
value_is
(
"test.some-int"
,
42
));
CAF_CHECK
(
value_is
(
"test.some-string"
,
"hi there!"
));
}
CAF_TEST_FIXTURE_SCOPE_END
()
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