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
d15392b5
Commit
d15392b5
authored
Nov 05, 2019
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Replace broken Pandoc setup with new custom parser
parent
66bad46d
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1417 additions
and
126 deletions
+1417
-126
.clang-format
.clang-format
+2
-0
doc/CMakeLists.txt
doc/CMakeLists.txt
+51
-52
doc/cmake/caf-generate-rst.cpp
doc/cmake/caf-generate-rst.cpp
+1238
-0
doc/tex/Brokers.tex
doc/tex/Brokers.tex
+1
-1
doc/tex/CommonPitfalls.tex
doc/tex/CommonPitfalls.tex
+45
-52
doc/tex/Introduction.tex
doc/tex/Introduction.tex
+1
-1
doc/tex/MessagePassing.tex
doc/tex/MessagePassing.tex
+8
-4
doc/tex/ReferenceCounting.tex
doc/tex/ReferenceCounting.tex
+1
-1
libcaf_core/caf/detail/variant_data.hpp
libcaf_core/caf/detail/variant_data.hpp
+49
-14
libcaf_core/caf/variant.hpp
libcaf_core/caf/variant.hpp
+21
-1
No files found.
.clang-format
View file @
d15392b5
...
@@ -32,6 +32,8 @@ IndentWidth: 2
...
@@ -32,6 +32,8 @@ IndentWidth: 2
IndentWrappedFunctionNames: false
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
Language: Cpp
MacroBlockBegin: "^BEGIN_STATE$"
MacroBlockEnd: "^END_STATE$"
MaxEmptyLinesToKeep: 1
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
NamespaceIndentation: None
PenaltyBreakAssignment: 1000
PenaltyBreakAssignment: 1000
...
...
doc/CMakeLists.txt
View file @
d15392b5
...
@@ -30,20 +30,22 @@ set(sources
...
@@ -30,20 +30,22 @@ set(sources
tex/Testing.tex
tex/Testing.tex
)
)
# -- process .in files -----------------------------------------------------
# -- create target folders -----------------------------------------------------
file
(
MAKE_DIRECTORY
"
${
CMAKE_CURRENT_BINARY_DIR
}
/tex"
)
file
(
MAKE_DIRECTORY
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst"
)
# -- process .in files ---------------------------------------------------------
configure_file
(
"cmake/Doxyfile.in"
configure_file
(
"cmake/Doxyfile.in"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/Doxyfile"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/Doxyfile"
@ONLY
)
@ONLY
)
file
(
MAKE_DIRECTORY
"
${
CMAKE_CURRENT_BINARY_DIR
}
/tex"
)
configure_file
(
"cmake/variables.tex.in"
configure_file
(
"cmake/variables.tex.in"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/tex/variables.tex"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/tex/variables.tex"
@ONLY
)
@ONLY
)
file
(
MAKE_DIRECTORY
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst"
)
configure_file
(
"cmake/conf.py.in"
configure_file
(
"cmake/conf.py.in"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst/conf.py"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst/conf.py"
@ONLY
)
@ONLY
)
...
@@ -56,6 +58,50 @@ configure_file("cmake/index_header.rst.in"
...
@@ -56,6 +58,50 @@ configure_file("cmake/index_header.rst.in"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst/index_header.rst"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst/index_header.rst"
@ONLY
)
@ONLY
)
# -- generate .rst files -------------------------------------------------------
add_executable
(
caf-generate-rst cmake/caf-generate-rst.cpp
)
target_link_libraries
(
caf-generate-rst
${
CAF_EXTRA_LDFLAGS
}
${
CAF_LIBRARIES
}
${
PTHREAD_LIBRARIES
}
)
add_custom_target
(
rst
)
add_dependencies
(
doc rst
)
function
(
convert_to_rst tex_file
)
get_filename_component
(
file_name
"
${
tex_file
}
"
NAME_WE
)
set
(
input
"
${
CMAKE_CURRENT_SOURCE_DIR
}
/tex/
${
tex_file
}
"
)
set
(
rst_file
"
${
file_name
}
.rst"
)
set
(
output
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst/
${
rst_file
}
"
)
add_custom_command
(
OUTPUT
"
${
output
}
"
COMMAND
caf-generate-rst
-o
"
${
output
}
"
-i
"
${
input
}
"
-r
"
${
PROJECT_SOURCE_DIR
}
"
DEPENDS caf-generate-rst
"
${
input
}
"
)
add_custom_target
(
"
${
rst_file
}
"
DEPENDS
"
${
output
}
"
)
add_dependencies
(
rst
"
${
rst_file
}
"
)
endfunction
()
foreach
(
filename
${
sources
}
)
get_filename_component
(
filename_no_dir
"
${
filename
}
"
NAME
)
convert_to_rst
(
"
${
filename_no_dir
}
"
)
endforeach
()
# generate index.rst file from manual.tex
add_custom_target
(
"index.rst"
DEPENDS
"tex/manual.tex"
COMMAND
"python"
"
${
CMAKE_SOURCE_DIR
}
/scripts/make_index_rst.py"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst/index.rst"
"
${
CMAKE_SOURCE_DIR
}
/doc/tex/manual.tex"
WORKING_DIRECTORY
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst"
)
add_dependencies
(
rst
"index.rst"
)
# -- Doxygen setup -------------------------------------------------------------
# -- Doxygen setup -------------------------------------------------------------
find_package
(
Doxygen
)
find_package
(
Doxygen
)
...
@@ -72,23 +118,6 @@ else()
...
@@ -72,23 +118,6 @@ else()
add_dependencies
(
doc doxygen
)
add_dependencies
(
doc doxygen
)
endif
()
endif
()
# -- Pandoc utility macro ------------------------------------------------------
macro
(
generate_rst texfile
)
get_filename_component
(
rstfile_we
"
${
texfile
}
"
NAME_WE
)
set
(
rstfile
"
${
rstfile_we
}
.rst"
)
set
(
bin_texfile
"
${
CMAKE_CURRENT_BINARY_DIR
}
/
${
texfile
}
"
)
add_custom_target
(
"
${
rstfile
}
"
DEPENDS
"
${
bin_texfile
}
"
COMMAND
${
PANDOC_EXECUTABLE
}
"--filter=
${
CMAKE_SOURCE_DIR
}
/scripts/pandoc-filter.py"
--wrap=none -f latex
-o
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst/
${
rstfile
}
"
"
${
bin_texfile
}
"
WORKING_DIRECTORY
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst"
)
add_dependencies
(
rst
"
${
rstfile
}
"
)
endmacro
()
# -- LaTeX setup ---------------------------------------------------------------
# -- LaTeX setup ---------------------------------------------------------------
if
(
CAF_BUILD_TEX_MANUAL
)
if
(
CAF_BUILD_TEX_MANUAL
)
...
@@ -104,35 +133,5 @@ if (CAF_BUILD_TEX_MANUAL)
...
@@ -104,35 +133,5 @@ if (CAF_BUILD_TEX_MANUAL)
FORCE_PDF
FORCE_PDF
TARGET_NAME manual
)
TARGET_NAME manual
)
add_dependencies
(
doc manual
)
add_dependencies
(
doc manual
)
find_program
(
PANDOC_EXECUTABLE pandoc
)
if
(
NOT EXISTS
${
PANDOC_EXECUTABLE
}
)
message
(
STATUS
"Pandoc not found, skip generating reFormattedText version of the manual."
)
else
()
execute_process
(
COMMAND
"python"
"-c"
"from pandocfilters import toJSONFilter; print('ok')"
RESULT_VARIABLE has_pandocfilters
OUTPUT_QUIET
ERROR_QUIET
)
if
(
NOT
${
has_pandocfilters
}
EQUAL 0
)
message
(
STATUS
"Python with pandocfilters not found, skip generating reFormattedText version of the manual."
)
else
()
message
(
STATUS
"Add optional target: rst."
)
add_custom_target
(
rst
)
add_dependencies
(
doc rst
)
# generate .rst files for individual sections
foreach
(
texfile
${
sources
}
)
generate_rst
(
${
texfile
}
)
endforeach
()
# generate index.rst file from manual.tex
add_custom_target
(
"index.rst"
DEPENDS
"tex/manual.tex"
COMMAND
"python"
"
${
CMAKE_SOURCE_DIR
}
/scripts/make_index_rst.py"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst/index.rst"
"
${
CMAKE_SOURCE_DIR
}
/doc/tex/manual.tex"
WORKING_DIRECTORY
"
${
CMAKE_CURRENT_BINARY_DIR
}
/rst"
)
add_dependencies
(
rst
"index.rst"
)
endif
()
endif
()
endif
()
endif
()
doc/cmake/caf-generate-rst.cpp
0 → 100644
View file @
d15392b5
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 <ctype.h>
#include <stack>
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/read_atom.hpp"
#include "caf/detail/parser/read_bool.hpp"
#include "caf/detail/parser/read_number_or_timespan.hpp"
#include "caf/detail/parser/read_string.hpp"
#include "caf/detail/parser/read_uri.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/exec_main.hpp"
#include "caf/pec.hpp"
#include "caf/uri_builder.hpp"
namespace
{
void
trim
(
std
::
string
&
s
)
{
auto
not_space
=
[](
char
c
)
{
return
isspace
(
c
)
==
0
;
};
s
.
erase
(
s
.
begin
(),
find_if
(
s
.
begin
(),
s
.
end
(),
not_space
));
s
.
erase
(
find_if
(
s
.
rbegin
(),
s
.
rend
(),
not_space
).
base
(),
s
.
end
());
}
template
<
class
>
struct
type_name
;
template
<
class
>
struct
is_inline
;
#define DECLARE_NAMED_STRUCT(type, inline_flag) \
struct type; \
template <> \
struct type_name<type> { \
static constexpr const char* value = #type; \
}; \
template <> \
struct is_inline<type> { \
static constexpr bool value = inline_flag; \
}
DECLARE_NAMED_STRUCT
(
section
,
false
);
DECLARE_NAMED_STRUCT
(
subsection
,
false
);
DECLARE_NAMED_STRUCT
(
subsubsection
,
false
);
DECLARE_NAMED_STRUCT
(
paragraph
,
false
);
DECLARE_NAMED_STRUCT
(
label
,
false
);
DECLARE_NAMED_STRUCT
(
see
,
true
);
DECLARE_NAMED_STRUCT
(
sref
,
true
);
DECLARE_NAMED_STRUCT
(
ref
,
true
);
DECLARE_NAMED_STRUCT
(
verbatim
,
false
);
DECLARE_NAMED_STRUCT
(
lstlisting
,
false
);
DECLARE_NAMED_STRUCT
(
lstinline
,
true
);
DECLARE_NAMED_STRUCT
(
text
,
true
);
DECLARE_NAMED_STRUCT
(
texttt
,
true
);
DECLARE_NAMED_STRUCT
(
textbf
,
true
);
DECLARE_NAMED_STRUCT
(
textit
,
true
);
DECLARE_NAMED_STRUCT
(
href
,
true
);
DECLARE_NAMED_STRUCT
(
item
,
false
);
DECLARE_NAMED_STRUCT
(
itemize
,
false
);
DECLARE_NAMED_STRUCT
(
enumerate
,
false
);
DECLARE_NAMED_STRUCT
(
tabular
,
false
);
DECLARE_NAMED_STRUCT
(
cppexample
,
false
);
DECLARE_NAMED_STRUCT
(
iniexample
,
false
);
DECLARE_NAMED_STRUCT
(
sourcefile
,
false
);
DECLARE_NAMED_STRUCT
(
singlefig
,
false
);
DECLARE_NAMED_STRUCT
(
experimental
,
true
);
using
node
=
caf
::
variant
<
section
,
subsection
,
subsubsection
,
paragraph
,
label
,
see
,
sref
,
ref
,
verbatim
,
lstlisting
,
lstinline
,
text
,
texttt
,
textbf
,
textit
,
href
,
item
,
itemize
,
enumerate
,
tabular
,
cppexample
,
iniexample
,
sourcefile
,
singlefig
,
experimental
>
;
struct
section
{
std
::
string
name
;
};
struct
subsection
{
std
::
string
name
;
};
struct
subsubsection
{
std
::
string
name
;
};
struct
paragraph
{
std
::
string
name
;
};
struct
label
{
std
::
string
name
;
};
struct
see
{
std
::
string
link
;
};
struct
sref
{
std
::
string
link
;
};
struct
ref
{
std
::
string
link
;
};
struct
verbatim
{
std
::
string
block
;
};
struct
lstlisting
{
std
::
string
block
;
};
struct
lstinline
{
std
::
string
str
;
};
struct
text
{
std
::
string
str
;
};
struct
texttt
{
std
::
string
str
;
};
struct
textbf
{
std
::
string
str
;
};
struct
textit
{
std
::
string
str
;
};
struct
href
{
std
::
string
url
;
std
::
string
str
;
};
struct
item
{
std
::
vector
<
node
>
nodes
;
};
struct
itemize
{
std
::
vector
<
item
>
items
;
};
struct
enumerate
{
std
::
vector
<
item
>
items
;
};
struct
tabular
{
using
column_type
=
item
;
using
row_type
=
std
::
vector
<
column_type
>
;
std
::
vector
<
row_type
>
rows
;
};
struct
cppexample
{
std
::
string
lines
;
std
::
string
file
;
};
struct
iniexample
{
std
::
string
lines
;
std
::
string
file
;
};
struct
sourcefile
{
std
::
string
lines
;
std
::
string
file
;
};
struct
singlefig
{
std
::
string
file
;
std
::
string
caption
;
std
::
string
label
;
};
struct
experimental
{
// nop
};
#define MAKE_NODE_0(type) \
if (name == #type) { \
if (args.size() != 0) \
throw std::runtime_error("expected exactly 0 argument for " #type \
", got: " \
+ caf::deep_to_string(args)); \
return type{}; \
}
#define MAKE_NODE_1(type) \
if (name == #type) { \
if (args.size() != 1) \
throw std::runtime_error("expected exactly 1 argument for " #type \
", got: " \
+ caf::deep_to_string(args)); \
return type{std::move(args[0])}; \
}
#define MAKE_NODE_2(type) \
if (name == #type) { \
if (args.size() != 2) \
throw std::runtime_error("expected exactly 2 argument for " #type \
", got: " \
+ caf::deep_to_string(args)); \
return type{std::move(args[0]), std::move(args[1])}; \
}
#define MAKE_NODE_3(type) \
if (name == #type) { \
if (args.size() != 3) \
throw std::runtime_error("expected exactly 3 argument for " #type \
", got: " \
+ caf::deep_to_string(args)); \
return type{std::move(args[0]), std::move(args[1]), std::move(args[2])}; \
}
#define MAKE_LINES_AND_FILE_NODE(type) \
if (name == #type) { \
if (args.size() == 1) { \
return type{std::string{}, std::move(args[0])}; \
} else if (args.size() == 2) { \
return type{std::move(args[0]), std::move(args[1])}; \
} \
throw std::runtime_error("expected 1 or 2 arguments for " #type); \
}
node
make_node
(
const
std
::
string
&
name
,
std
::
vector
<
std
::
string
>
args
)
{
MAKE_NODE_1
(
section
)
MAKE_NODE_1
(
subsection
)
MAKE_NODE_1
(
subsubsection
)
MAKE_NODE_1
(
paragraph
)
MAKE_NODE_1
(
label
)
MAKE_NODE_1
(
see
)
MAKE_NODE_1
(
sref
)
MAKE_NODE_1
(
ref
)
MAKE_NODE_1
(
verbatim
)
MAKE_NODE_1
(
lstlisting
)
MAKE_NODE_1
(
lstinline
)
MAKE_NODE_1
(
texttt
)
MAKE_NODE_1
(
textbf
)
MAKE_NODE_1
(
textit
)
MAKE_NODE_2
(
href
)
MAKE_LINES_AND_FILE_NODE
(
cppexample
)
MAKE_LINES_AND_FILE_NODE
(
iniexample
)
MAKE_LINES_AND_FILE_NODE
(
sourcefile
)
MAKE_NODE_3
(
singlefig
)
MAKE_NODE_0
(
experimental
)
if
(
name
==
"emph"
)
return
make_node
(
"textit"
,
std
::
move
(
args
));
throw
std
::
runtime_error
(
"unrecognized command: "
+
name
+
caf
::
deep_to_string
(
args
));
}
bool
is_ignored_node
(
const
std
::
string
&
name
,
const
std
::
vector
<
std
::
string
>&
args
)
{
if
(
args
.
size
()
==
0
)
return
name
==
"clearpage"
||
name
==
"textwidth"
;
if
(
args
.
size
()
==
1
)
return
(
name
==
"begin"
||
name
==
"end"
)
&&
(
args
[
0
]
==
"center"
||
args
[
0
]
==
"footnotesize"
);
return
false
;
}
struct
abstract_consumer
{
public:
virtual
~
abstract_consumer
()
{
// nop
}
virtual
void
consume
(
node
x
)
=
0
;
};
template
<
class
Result
>
struct
list_builder
:
abstract_consumer
{
using
result_type
=
Result
;
abstract_consumer
*
consumer
;
Result
result
;
bool
finalized
=
false
;
explicit
list_builder
(
abstract_consumer
*
ptr
)
:
consumer
(
ptr
)
{
// nop
}
void
finalize
()
{
consumer
->
consume
(
std
::
move
(
result
));
finalized
=
true
;
}
void
consume
(
node
x
)
override
{
// The command parser might pass whitespaces to this builder after seeing
// the end command.
if
(
finalized
)
{
consumer
->
consume
(
std
::
move
(
x
));
return
;
}
if
(
result
.
items
.
empty
())
throw
std
::
runtime_error
(
"expected
\\
item as first token for list block"
);
result
.
items
.
back
().
nodes
.
emplace_back
(
std
::
move
(
x
));
}
void
cmd
(
const
std
::
string
&
name
,
std
::
vector
<
std
::
string
>
args
)
{
if
(
name
==
"end"
&&
args
.
size
()
==
1
&&
args
[
0
]
==
type_name
<
Result
>::
value
)
{
finalize
();
}
else
if
(
name
==
"item"
)
{
result
.
items
.
emplace_back
();
}
else
{
consumer
->
consume
(
make_node
(
name
,
std
::
move
(
args
)));
}
}
};
struct
tabular_builder
:
abstract_consumer
{
abstract_consumer
*
consumer
;
tabular
result
;
bool
finalized
=
false
;
explicit
tabular_builder
(
abstract_consumer
*
consumer
)
:
consumer
(
consumer
)
{
next_row
();
}
void
consume
(
node
x
)
override
{
// The command parser might pass whitespaces to this builder after seeing
// the end command.
if
(
finalized
)
consumer
->
consume
(
std
::
move
(
x
));
else
result
.
rows
.
back
().
back
().
nodes
.
emplace_back
(
std
::
move
(
x
));
}
void
next_column
()
{
result
.
rows
.
back
().
emplace_back
();
}
void
next_row
()
{
result
.
rows
.
emplace_back
();
next_column
();
}
void
cmd
(
const
std
::
string
&
name
,
std
::
vector
<
std
::
string
>
args
)
{
if
(
name
==
"hline"
)
{
// drop
}
else
if
(
name
==
"end"
&&
args
.
size
()
==
1
&&
args
[
0
]
==
"tabular"
)
{
if
(
result
.
rows
.
back
().
empty
())
{
result
.
rows
.
pop_back
();
if
(
result
.
rows
.
empty
())
throw
std
::
runtime_error
(
"empty table"
);
}
consumer
->
consume
(
std
::
move
(
result
));
finalized
=
true
;
}
else
{
consume
(
make_node
(
name
,
std
::
move
(
args
)));
}
}
};
}
// namespace
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
namespace
{
using
std
::
string
;
using
namespace
caf
;
using
namespace
caf
::
detail
;
using
namespace
caf
::
detail
::
parser
;
template
<
class
State
,
class
Consumer
>
void
read_tex_comment
(
State
&
ps
,
Consumer
&&
)
{
start
();
term_state
(
init
){
transition
(
done
,
'\n'
)
transition
(
init
)}
term_state
(
done
)
{
// nop
}
fin
();
}
template
<
class
State
,
class
Consumer
>
void
read_tex_verbatim
(
State
&
ps
,
Consumer
&&
consumer
,
const
string
&
cmd_name
)
{
string
verbatim
;
string
cmd
;
string
end_of_command
=
"end{"
+
cmd_name
;
auto
flush_cmd
=
[
&
]
{
verbatim
+=
'\\'
;
verbatim
+=
cmd
;
cmd
.
clear
();
};
auto
guard
=
make_scope_guard
([
&
]
{
if
(
ps
.
code
<=
pec
::
trailing_character
)
{
std
::
vector
<
string
>
args
;
args
.
emplace_back
(
std
::
move
(
verbatim
));
consumer
.
cmd
(
cmd_name
,
std
::
move
(
args
));
}
});
// clang-format off
start
();
state
(
init
)
{
transition
(
read_end_verbatim
,
"
\\
"
);
transition
(
init
,
any_char
,
verbatim
+=
ch
)
}
state
(
read_end_verbatim
)
{
transition_if
(
cmd
==
end_of_command
,
done
,
"}"
)
transition
(
init
,
"}"
,
flush_cmd
());
transition
(
read_end_verbatim
,
"
\\
"
,
flush_cmd
());
transition
(
read_end_verbatim
,
any_char
,
cmd
+=
ch
)
}
term_state
(
done
)
{
// nop
}
fin
();
// clang-format on
}
template
<
class
State
,
class
Consumer
>
void
read_tex_list
(
State
&
ps
,
Consumer
&&
consumer
,
const
string
&
cmd_name
);
template
<
class
State
,
class
Consumer
>
void
read_tex_tabular
(
State
&
ps
,
Consumer
&&
consumer
);
template
<
class
State
,
class
Consumer
>
void
read_tex_command
(
State
&
ps
,
Consumer
&&
consumer
)
{
string
cmd
;
string
spaces
;
std
::
vector
<
string
>
args
;
const
char
*
stop_chars
;
auto
add_arg
=
[
&
](
const
char
*
closing_char
)
{
spaces
.
clear
();
stop_chars
=
closing_char
;
args
.
emplace_back
();
};
auto
is_verbatim_cmd
=
[
&
]
{
if
(
args
.
size
()
!=
1
)
return
false
;
return
cmd
==
"begin"
&&
(
args
[
0
]
==
"verbatim"
||
args
[
0
]
==
"lstlisting"
);
};
auto
is_list_cmd
=
[
&
]
{
if
(
args
.
size
()
!=
1
)
return
false
;
return
cmd
==
"begin"
&&
(
args
[
0
]
==
"itemize"
||
args
[
0
]
==
"enumerate"
);
};
auto
is_tabular_cmd
=
[
&
]
{
if
(
args
.
size
()
!=
2
)
return
false
;
return
cmd
==
"begin"
&&
args
[
0
]
==
"tabular"
;
};
auto
guard
=
make_scope_guard
([
&
]
{
if
(
is_ignored_node
(
cmd
,
args
))
return
;
if
(
cmd
.
empty
())
{
ps
.
code
=
pec
::
unexpected_eof
;
return
;
}
if
(
ps
.
code
<=
pec
::
trailing_character
)
{
consumer
.
cmd
(
cmd
,
std
::
move
(
args
));
if
(
!
spaces
.
empty
())
consumer
.
consume
(
text
{
std
::
move
(
spaces
)});
}
});
// clang-format off
start
();
state
(
init
)
{
epsilon
(
read_command
)
}
term_state
(
read_command
)
{
fsm_epsilon
(
read_tex_comment
(
ps
,
consumer
),
read_command
,
'%'
)
fsm_epsilon_if
(
is_verbatim_cmd
(),
read_tex_verbatim
(
ps
,
consumer
,
args
[
0
]),
done
,
any_char
)
fsm_epsilon_if
(
is_list_cmd
(),
read_tex_list
(
ps
,
consumer
,
args
[
0
]),
done
,
any_char
)
fsm_epsilon_if
(
is_tabular_cmd
(),
read_tex_tabular
(
ps
,
consumer
),
done
,
any_char
)
transition_if
(
args
.
empty
()
&&
spaces
.
empty
(),
read_command
,
alphanumeric_chars
,
cmd
+=
ch
)
transition
(
read_command_arg
,
"["
,
add_arg
(
"]"
))
transition
(
read_command_arg
,
"{"
,
add_arg
(
"}"
))
transition
(
read_command_arg
,
"`"
,
add_arg
(
"`"
))
transition
(
read_command_arg
,
"^"
,
add_arg
(
"^"
))
transition
(
read_command
,
"
\t\n
"
,
spaces
+=
ch
)
}
state
(
read_command_arg
)
{
fsm_epsilon
(
read_tex_comment
(
ps
,
consumer
),
read_command_arg
,
'%'
)
transition
(
read_command
,
stop_chars
)
transition
(
read_command_arg
,
any_char
,
args
.
back
()
+=
ch
)
}
term_state
(
done
)
{
guard
.
disable
();
}
fin
();
// clang-format on
}
/// Reads an .tex formatted input file for the CAF manual.
template
<
class
State
,
class
Consumer
>
void
read_tex
(
State
&
ps
,
Consumer
&&
consumer
)
{
string
str
;
auto
consume_str
=
[
&
]
{
if
(
!
str
.
empty
())
{
consumer
.
consume
(
text
{
std
::
move
(
str
)});
str
.
clear
();
}
};
auto
guard
=
make_scope_guard
([
&
]
{
if
(
ps
.
code
<=
pec
::
trailing_character
)
consume_str
();
});
// clang-format off
start
();
term_state
(
init
)
{
fsm_transition
(
read_tex_comment
(
ps
,
consumer
),
init
,
'%'
)
transition
(
start_escaping
,
"
\\
"
)
transition
(
init
,
"~"
,
str
+=
' '
)
transition
(
init
,
any_char
,
str
+=
ch
)
}
state
(
start_escaping
)
{
transition
(
init
,
"
\\
"
,
str
+=
'\\'
)
transition
(
init
,
"%"
,
str
+=
'%'
)
fsm_epsilon
(
read_tex_command
(
ps
,
consumer
),
init
,
any_char
,
consume_str
())
}
fin
();
// clang-format on
}
template
<
class
State
,
class
Consumer
>
void
read_tex_list
(
State
&
ps
,
Consumer
&
consumer
)
{
string
str
;
auto
consume_str
=
[
&
]
{
if
(
!
str
.
empty
())
{
consumer
.
consume
(
text
{
std
::
move
(
str
)});
str
.
clear
();
}
};
auto
before_first_item
=
[
&
]
{
return
consumer
.
result
.
items
.
empty
();
};
// clang-format off
start
();
state
(
init
)
{
fsm_transition
(
read_tex_comment
(
ps
,
consumer
),
init
,
'%'
)
transition
(
start_escaping
,
"
\\
"
)
transition_if
(
!
before_first_item
(),
init
,
"~"
,
str
+=
' '
)
transition_if
(
!
before_first_item
(),
init
,
any_char
,
str
+=
ch
)
transition_if
(
before_first_item
(),
init
,
"
\t\n
"
)
}
state
(
start_escaping
)
{
transition_if
(
!
before_first_item
(),
init
,
"
\\
"
,
str
+=
'\\'
)
transition_if
(
!
before_first_item
(),
init
,
"%"
,
str
+=
'%'
)
fsm_epsilon
(
read_tex_command
(
ps
,
consumer
),
after_cmd
,
any_char
,
consume_str
())
}
unstable_state
(
after_cmd
)
{
epsilon_if
(
consumer
.
finalized
,
done
,
any_char
)
epsilon
(
init
,
any_char
)
}
term_state
(
done
)
{
// nop
}
fin
();
// clang-format on
}
template
<
class
State
,
class
Consumer
>
void
read_tex_list
(
State
&
ps
,
Consumer
&
consumer
,
const
string
&
cmd_name
)
{
if
(
cmd_name
==
"itemize"
)
{
list_builder
<
itemize
>
builder
{
&
consumer
};
read_tex_list
(
ps
,
builder
);
}
else
if
(
cmd_name
==
"enumerate"
)
{
list_builder
<
enumerate
>
builder
{
&
consumer
};
read_tex_list
(
ps
,
builder
);
}
else
{
throw
std
::
logic_error
(
"expected itemize or enumerate"
);
}
}
template
<
class
State
,
class
Consumer
>
void
read_tex_tabular
(
State
&
ps
,
Consumer
&&
consumer
)
{
tabular_builder
builder
{
&
consumer
};
string
str
;
auto
consume_str
=
[
&
]
{
if
(
!
str
.
empty
())
{
builder
.
consume
(
text
{
str
});
str
.
clear
();
}
};
auto
next_column
=
[
&
]
{
consume_str
();
builder
.
next_column
();
};
auto
next_row
=
[
&
]
{
consume_str
();
builder
.
next_row
();
};
// clang-format off
start
();
state
(
init
)
{
fsm_transition
(
read_tex_comment
(
ps
,
builder
),
init
,
'%'
)
transition
(
start_escaping
,
"
\\
"
)
transition
(
init
,
"&"
,
next_column
())
transition
(
init
,
"~"
,
str
+=
' '
)
transition
(
init
,
any_char
,
str
+=
ch
)
}
state
(
start_escaping
)
{
transition
(
init
,
"
\\
"
,
next_row
())
transition
(
init
,
"%"
,
str
+=
'%'
)
fsm_epsilon
(
read_tex_command
(
ps
,
builder
),
after_cmd
,
any_char
,
consume_str
())
}
unstable_state
(
after_cmd
)
{
epsilon_if
(
builder
.
finalized
,
done
,
any_char
)
epsilon
(
init
,
any_char
)
}
term_state
(
done
)
{
// nop
}
fin
();
// clang-format on
}
}
// namespace
#include "caf/detail/parser/fsm_undef.hpp"
CAF_POP_WARNINGS
namespace
{
struct
file_iter
{
std
::
istream
*
f
;
char
ch
;
explicit
file_iter
(
std
::
istream
*
f
)
:
f
(
f
)
{
f
->
get
(
ch
);
}
file_iter
()
:
f
(
nullptr
),
ch
(
'\0'
)
{
// nop
}
file_iter
(
const
file_iter
&
)
=
default
;
file_iter
&
operator
=
(
const
file_iter
&
)
=
default
;
char
operator
*
()
const
{
return
ch
;
}
file_iter
&
operator
++
()
{
f
->
get
(
ch
);
return
*
this
;
}
};
struct
file_sentinel
{};
bool
operator
!=
(
file_iter
iter
,
file_sentinel
)
{
return
!
iter
.
f
->
fail
();
}
class
rst_writer
;
class
rst_writer_state
:
public
abstract_consumer
{
public:
rst_writer_state
(
rst_writer
*
parent
)
:
parent_
(
parent
)
{
// nop
}
virtual
~
rst_writer_state
()
{
// nop
}
virtual
const
char
*
name
()
const
noexcept
=
0
;
virtual
void
exit
();
rst_writer
*
parent
()
const
noexcept
{
return
parent_
;
};
std
::
ostream
&
out
();
protected:
rst_writer
*
parent_
;
};
struct
parse_failure
:
std
::
runtime_error
{
using
super
=
std
::
runtime_error
;
const
char
*
state_name
;
parse_failure
(
const
char
*
state_name
,
const
std
::
string
&
what
)
:
super
(
what
),
state_name
(
state_name
)
{
// nop
}
template
<
class
...
Ts
>
[[
noreturn
]]
static
void
raise
(
const
char
*
state_name
,
const
Ts
&
...
xs
)
{
throw
parse_failure
(
state_name
,
deep_to_string
(
std
::
forward_as_tuple
(
xs
...)));
}
};
template
<
class
Subtype
>
class
rst_writer_state_base
:
public
rst_writer_state
{
public:
using
rst_writer_state
::
rst_writer_state
;
void
consume
(
node
x
)
override
{
current_node_
=
&
x
;
visit
(
dref
(),
x
);
current_node_
=
nullptr
;
}
template
<
class
T
,
class
...
Ts
>
void
epsilon
(
const
std
::
unique_ptr
<
T
>&
target
,
Ts
&&
...
xs
)
{
auto
&
st
=
target
->
parent
()
->
state
;
if
(
st
)
st
->
exit
();
st
=
target
.
get
();
target
->
entry
(
std
::
forward
<
Ts
>
(
xs
)...);
target
->
consume
(
std
::
move
(
*
current_node_
));
}
template
<
class
T
>
void
operator
()(
T
&
)
{
auto
cstr
=
type_name
<
T
>::
value
;
parse_failure
::
raise
(
dref
().
name
(),
"unexpected command: "
,
cstr
);
}
private:
Subtype
&
dref
()
{
return
static_cast
<
Subtype
&>
(
*
this
);
}
node
*
current_node_
=
nullptr
;
};
template
<
class
T
,
class
...
Ts
>
void
transition
(
const
std
::
unique_ptr
<
T
>&
target
,
Ts
&&
...
xs
)
{
auto
&
st
=
target
->
parent
()
->
state
;
if
(
st
)
st
->
exit
();
st
=
target
.
get
();
target
->
entry
(
std
::
forward
<
Ts
>
(
xs
)...);
}
#define DECLARE_STATE(name) \
struct name##_state; \
std::unique_ptr<name##_state> name; \
void make_##name()
class
rst_writer
:
public
abstract_consumer
{
public:
using
state_ptr
=
std
::
unique_ptr
<
rst_writer_state
>
;
rst_writer
()
:
state
(
nullptr
)
{
make_await_section
();
make_await_section_label
();
make_read_body
();
transition
(
await_section
);
}
~
rst_writer
()
{
state
->
exit
();
}
void
consume
(
node
x
)
override
{
state
->
consume
(
std
::
move
(
x
));
}
void
cmd
(
const
std
::
string
&
name
,
std
::
vector
<
std
::
string
>
args
)
{
consume
(
make_node
(
name
,
std
::
move
(
args
)));
}
std
::
string
project_root
;
rst_writer_state
*
state
;
std
::
ofstream
out
;
DECLARE_STATE
(
await_section
);
DECLARE_STATE
(
await_section_label
);
DECLARE_STATE
(
read_body
);
};
std
::
ostream
&
rst_writer_state
::
out
()
{
return
parent_
->
out
;
}
void
rst_writer_state
::
exit
()
{
// customization point
}
#define BEGIN_STATE(type) \
struct rst_writer::type##_state \
: rst_writer_state_base<rst_writer::type##_state> { \
using super = rst_writer_state_base<rst_writer::type##_state>; \
type##_state(rst_writer* parent) : super(parent) { \
} \
using super::operator(); \
const char* name() const noexcept override { \
return #type; \
}
#define END_STATE(type) \
} \
; \
void rst_writer::make_##type() { \
type.reset(new type##_state(this)); \
}
BEGIN_STATE
(
await_section
)
void
entry
()
{
// nop
}
void
operator
()(
section
&
x
)
{
transition
(
parent_
->
await_section_label
,
x
.
name
,
'='
);
}
END_STATE
(
await_section
)
BEGIN_STATE
(
await_section_label
)
void
entry
(
const
std
::
string
&
section_name
,
char
highlighting
)
{
spaces
.
clear
();
this
->
section_name
=
section_name
;
this
->
highlighting
=
highlighting
;
}
void
operator
()(
label
&
x
)
{
out
()
<<
".. _"
<<
x
.
name
<<
":
\n\n
"
<<
section_name
<<
'\n'
<<
std
::
string
(
section_name
.
size
(),
highlighting
)
<<
"
\n\n
"
;
transition
(
parent_
->
read_body
);
}
void
operator
()(
text
&
x
)
{
// Ignore whitespaces between \section and \label commands.
if
(
x
.
str
.
empty
())
return
;
if
(
!
spaces
.
empty
())
{
x
.
str
.
insert
(
x
.
str
.
begin
(),
spaces
.
begin
(),
spaces
.
end
());
delegate
();
return
;
}
if
(
std
::
all_of
(
x
.
str
.
begin
(),
x
.
str
.
end
(),
::
isspace
))
spaces
=
std
::
move
(
x
.
str
);
else
delegate
();
}
template
<
class
T
>
void
operator
()(
T
&
)
{
delegate
();
}
void
delegate
()
{
out
()
<<
section_name
<<
'\n'
<<
std
::
string
(
section_name
.
size
(),
highlighting
)
<<
"
\n\n
"
;
epsilon
(
parent_
->
read_body
);
}
std
::
string
section_name
;
std
::
string
spaces
;
char
highlighting
;
END_STATE
(
await_section_label
)
struct
string_stream
{
std
::
string
&
result
;
};
string_stream
&
operator
<<
(
string_stream
&
out
,
string_view
str
)
{
out
.
result
.
insert
(
out
.
result
.
end
(),
str
.
begin
(),
str
.
end
());
return
out
;
}
string_stream
&
operator
<<
(
string_stream
&
out
,
char
c
)
{
out
.
result
+=
c
;
return
out
;
}
namespace
rst_ops
{
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
text
&
x
)
{
// Trim all whitespaces on the left and right but one.
if
(
x
.
str
.
empty
())
return
out
;
auto
no_space
=
[](
char
c
)
{
return
c
!=
' '
;
};
auto
i
=
std
::
find_if
(
x
.
str
.
begin
(),
x
.
str
.
end
(),
no_space
);
auto
e
=
std
::
find_if
(
x
.
str
.
rbegin
(),
x
.
str
.
rend
(),
no_space
).
base
();
if
(
i
>
e
)
return
out
<<
' '
;
if
(
i
!=
x
.
str
.
begin
())
out
<<
' '
;
for
(;
i
!=
e
;
++
i
)
out
<<
*
i
;
if
(
e
!=
x
.
str
.
end
())
out
<<
' '
;
return
out
;
}
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
see
&
x
)
{
return
out
<<
x
.
link
<<
'_'
;
}
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
sref
&
x
)
{
return
out
<<
x
.
link
<<
'_'
;
}
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
ref
&
x
)
{
return
out
<<
x
.
link
<<
'_'
;
}
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
lstinline
&
x
)
{
return
out
<<
"``"
<<
x
.
str
<<
"``"
;
}
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
texttt
&
x
)
{
return
out
<<
"``"
<<
x
.
str
<<
"``"
;
}
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
textbf
&
x
)
{
return
out
<<
"**"
<<
x
.
str
<<
"**"
;
}
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
textit
&
x
)
{
return
out
<<
"*"
<<
x
.
str
<<
"*"
;
}
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
href
&
x
)
{
return
out
<<
"`"
<<
x
.
str
<<
" <"
<<
x
.
url
<<
">`_"
;
}
template
<
class
Out
>
Out
&
operator
<<
(
Out
&
out
,
experimental
&
)
{
return
out
<<
"
\\
:sup:`experimental`
\\
"
;
}
}
// namespace rst_ops
template
<
class
Out
>
struct
rst_ops_visitor
{
Out
&
out
;
template
<
class
T
>
detail
::
enable_if_t
<
is_inline
<
T
>::
value
>
operator
()(
T
&
x
)
{
using
namespace
rst_ops
;
out
<<
x
;
}
template
<
class
T
>
detail
::
enable_if_t
<!
is_inline
<
T
>::
value
>
operator
()(
T
&
)
{
throw
std
::
runtime_error
(
"expected an inline command"
);
}
};
BEGIN_STATE
(
read_body
)
void
entry
()
{
// nop
}
template
<
class
T
>
detail
::
enable_if_t
<
is_inline
<
T
>::
value
>
operator
()(
T
&
x
)
{
using
namespace
rst_ops
;
out
()
<<
x
;
}
template
<
class
T
>
detail
::
enable_if_t
<!
is_inline
<
T
>::
value
>
operator
()(
T
&
x
)
{
super
::
operator
()(
x
);
}
void
operator
()(
subsection
&
x
)
{
transition
(
parent_
->
await_section_label
,
x
.
name
,
'-'
);
}
void
operator
()(
subsubsection
&
x
)
{
transition
(
parent_
->
await_section_label
,
x
.
name
,
'~'
);
}
void
operator
()(
paragraph
&
x
)
{
transition
(
parent_
->
await_section_label
,
x
.
name
,
'+'
);
}
void
operator
()(
lstlisting
&
x
)
{
print_block
(
".. code-block:: C++"
,
x
.
block
);
}
void
operator
()(
verbatim
&
x
)
{
print_block
(
".. ::"
,
x
.
block
);
}
void
operator
()(
itemize
&
x
)
{
out
()
<<
"
\n\n
"
;
for
(
auto
&
i
:
x
.
items
)
{
out
()
<<
"* "
;
for
(
auto
&
n
:
i
.
nodes
)
visit
(
*
this
,
n
);
out
()
<<
'\n'
;
}
out
()
<<
'\n'
;
}
void
operator
()(
enumerate
&
x
)
{
size_t
num
=
1
;
out
()
<<
"
\n\n
"
;
for
(
auto
&
i
:
x
.
items
)
{
out
()
<<
num
++
<<
". "
;
for
(
auto
&
n
:
i
.
nodes
)
visit
(
*
this
,
n
);
out
()
<<
'\n'
;
}
out
()
<<
'\n'
;
}
void
operator
()(
tabular
&
x
)
{
if
(
x
.
rows
.
empty
()
||
x
.
rows
[
0
].
empty
())
throw
std
::
runtime_error
(
"empty tabular"
);
// Convert the tabular into a string matrix.
std
::
vector
<
std
::
vector
<
std
::
string
>>
content
;
content
.
reserve
(
x
.
rows
.
size
());
auto
num_columns
=
x
.
rows
[
0
].
size
();
std
::
vector
<
size_t
>
column_sizes
(
num_columns
);
for
(
auto
&
row
:
x
.
rows
)
{
// This hack makes sure we can handle \hline on the last line of a
// tabular. It silently drops anything with different column side, but a
// proper fix is not trivial.
if
(
row
.
size
()
!=
num_columns
)
continue
;
content
.
emplace_back
();
auto
&
content_row
=
content
.
back
();
content_row
.
resize
(
num_columns
);
for
(
size_t
col_index
=
0
;
col_index
<
num_columns
;
++
col_index
)
{
auto
&
cell
=
content_row
[
col_index
];
string_stream
cell_out
{
cell
};
rst_ops_visitor
<
string_stream
>
v
{
cell_out
};
for
(
auto
&
node
:
row
[
col_index
].
nodes
)
visit
(
v
,
node
);
trim
(
cell
);
column_sizes
[
col_index
]
=
std
::
max
(
column_sizes
[
col_index
],
cell
.
size
());
}
}
// Output the matrix.
auto
hline
=
[
&
]
{
for
(
auto
cs
:
column_sizes
)
{
out
()
<<
"+-"
;
for
(
size_t
i
=
0
;
i
<
cs
;
++
i
)
out
()
<<
'-'
;
}
out
()
<<
"-+
\n
"
;
};
out
()
<<
"
\n\n
"
;
hline
();
for
(
auto
&
row
:
content
)
{
for
(
size_t
col_index
=
0
;
col_index
<
row
.
size
();
++
col_index
)
{
auto
&
column
=
row
[
col_index
];
column
.
resize
(
column_sizes
[
col_index
],
' '
);
out
()
<<
"| "
<<
column
;
}
out
()
<<
" |
\n
"
;
hline
();
}
out
()
<<
'\n'
;
}
void
operator
()(
cppexample
&
x
)
{
auto
path
=
parent_
->
project_root
;
path
+=
"/examples/"
;
path
+=
x
.
file
;
path
+=
".cpp"
;
std
::
ifstream
in
{
path
};
print_file
(
".. code-block:: c++"
,
in
,
x
.
lines
);
}
void
operator
()(
iniexample
&
x
)
{
auto
path
=
parent_
->
project_root
;
path
+=
"/examples/"
;
path
+=
x
.
file
;
path
+=
".ini"
;
std
::
ifstream
in
{
path
};
print_file
(
".. code-block:: ini"
,
in
,
x
.
lines
);
}
void
operator
()(
sourcefile
&
x
)
{
auto
path
=
parent_
->
project_root
;
path
+=
'/'
;
path
+=
x
.
file
;
std
::
ifstream
in
{
path
};
print_file
(
".. code-block:: c++"
,
in
,
x
.
lines
);
}
void
operator
()(
singlefig
&
x
)
{
out
()
<<
".. _"
<<
x
.
label
<<
":
\n\n
"
<<
".. image:: "
<<
x
.
file
<<
".png"
<<
'\n'
<<
" :alt: "
<<
x
.
caption
<<
"
\n\n
"
;
}
void
print_block
(
const
char
*
hdr
,
std
::
string
&
block
)
{
// Trim leading and trailing newlines.
while
(
block
.
size
()
>
0
&&
block
.
front
()
==
'\n'
)
block
.
erase
(
block
.
begin
());
while
(
block
.
size
()
>
0
&&
block
.
back
()
==
'\n'
)
block
.
pop_back
();
out
()
<<
"
\n
"
<<
hdr
<<
"
\n\n
"
;
out
()
<<
" "
;
for
(
auto
ch
:
block
)
{
if
(
ch
==
'\n'
)
out
()
<<
"
\n
"
;
else
out
()
<<
ch
;
}
out
()
<<
"
\n\n
"
;
}
void
print_file
(
const
char
*
hdr
,
std
::
ifstream
&
in
,
int
first_line
,
int
last_line
,
int
&
line_num
)
{
std
::
string
line
;
while
(
line_num
<
first_line
)
{
if
(
!
std
::
getline
(
in
,
line
))
throw
std
::
runtime_error
(
"unexpected end of file"
);
++
line_num
;
}
out
()
<<
"
\n
"
<<
hdr
<<
"
\n\n
"
;
while
(
line_num
<
last_line
)
{
if
(
!
std
::
getline
(
in
,
line
))
break
;
out
()
<<
" "
<<
line
<<
'\n'
;
++
line_num
;
}
out
()
<<
"
\n\n
"
;
}
void
print_file
(
const
char
*
hdr
,
std
::
ifstream
&
in
,
const
std
::
string
&
lines
)
{
int
line_num
=
1
;
if
(
lines
.
empty
())
{
print_file
(
hdr
,
in
,
1
,
std
::
numeric_limits
<
int
>::
max
(),
line_num
);
return
;
}
std
::
vector
<
std
::
string
>
line_ranges
;
split
(
line_ranges
,
lines
,
","
);
for
(
const
auto
&
lr
:
line_ranges
)
{
std
::
vector
<
std
::
string
>
line_nums
;
split
(
line_nums
,
lr
,
"-"
);
if
(
line_nums
.
size
()
!=
2
)
throw
std
::
runtime_error
(
"illegal line range"
);
print_file
(
hdr
,
in
,
std
::
stoi
(
line_nums
[
0
]),
std
::
stoi
(
line_nums
[
1
]),
line_num
);
}
}
END_STATE
(
read_body
)
struct
config
:
actor_system_config
{
config
()
{
opt_group
{
custom_options_
,
"global"
}
.
add
(
input
,
"input,i"
,
"input .tex file"
)
.
add
(
output
,
"output,o"
,
"output .rst file"
)
.
add
(
project_root
,
"project-root,r"
,
"project root for C++ examples"
);
set
(
"logger.verbosity"
,
atom
(
"quiet"
));
set
(
"scheduler.max-threads"
,
1
);
}
std
::
string
input
;
std
::
string
output
;
std
::
string
project_root
;
};
void
caf_main
(
actor_system
&
,
const
config
&
cfg
)
{
if
(
cfg
.
input
.
empty
()
||
cfg
.
output
.
empty
())
{
std
::
cerr
<<
"input or output path missing
\n
"
;
exit
(
EXIT_FAILURE
);
}
rst_writer
consumer
;
consumer
.
out
.
open
(
cfg
.
output
);
if
(
!
consumer
.
out
)
{
std
::
cerr
<<
"unable to open output file: "
<<
cfg
.
output
<<
'\n'
;
exit
(
EXIT_FAILURE
);
}
consumer
.
project_root
=
cfg
.
project_root
;
std
::
ifstream
input
{
cfg
.
input
};
parser_state
<
file_iter
,
file_sentinel
>
res
{
file_iter
{
&
input
}};
try
{
read_tex
(
res
,
consumer
);
if
(
res
.
i
!=
res
.
e
)
{
std
::
cerr
<<
"error in line "
<<
res
.
line
<<
" on column "
<<
res
.
column
<<
": "
<<
to_string
(
res
.
code
)
<<
'\n'
;
exit
(
EXIT_FAILURE
);
}
}
catch
(
parse_failure
&
x
)
{
std
::
cerr
<<
"error in line "
<<
res
.
line
<<
" on column "
<<
res
.
column
<<
" while in state "
<<
x
.
state_name
<<
": "
<<
x
.
what
()
<<
'\n'
;
exit
(
EXIT_FAILURE
);
}
catch
(
std
::
exception
&
x
)
{
std
::
cerr
<<
"error in line "
<<
res
.
line
<<
" on column "
<<
res
.
column
<<
": "
<<
x
.
what
()
<<
'\n'
;
exit
(
EXIT_FAILURE
);
}
catch
(...)
{
std
::
cerr
<<
"unknown error in line "
<<
res
.
line
<<
" on column "
<<
res
.
column
<<
'\n'
;
exit
(
EXIT_FAILURE
);
}
}
}
// namespace
CAF_MAIN
()
doc/tex/Brokers.tex
View file @
d15392b5
...
@@ -40,7 +40,7 @@ TCP and returns a broker managing this connection on success. Finally,
...
@@ -40,7 +40,7 @@ TCP and returns a broker managing this connection on success. Finally,
on success. There are no convenience functions spawn a UDP-based client or
on success. There are no convenience functions spawn a UDP-based client or
server.
server.
\subsection
{
Class
\
texttt
{
broker
}
}
\subsection
{
Class
\
lstinline
^
broker
^
}
\label
{
broker-class
}
\label
{
broker-class
}
\begin{lstlisting}
\begin{lstlisting}
...
...
doc/tex/CommonPitfalls.tex
View file @
d15392b5
...
@@ -6,66 +6,59 @@ programming in CAF.
...
@@ -6,66 +6,59 @@ programming in CAF.
\subsection
{
Defining Message Handlers
}
\subsection
{
Defining Message Handlers
}
\begin{itemize}
C++ evaluates comma-separated expressions from left-to-right, using only the
\item
C++ evaluates comma-separated expressions from left-to-right, using only
last element as return type of the whole expression. This means that message
the last element as return type of the whole expression. This means that
handlers and behaviors must
\emph
{
not
}
be initialized like this:
message handlers and behaviors must
\emph
{
not
}
be initialized like this:
\begin{lstlisting}
message
_
handler wrong = (
[](int i)
{
/*...*/
}
,
[](float f)
{
/*...*/
}
);
\end{lstlisting}
The correct way to initialize message handlers and behaviors is to either
use the constructor or the member function
\lstinline
^
assign
^
:
\begin{lstlisting}
message
_
handler ok1
{
[](int i)
{
/*...*/
}
,
[](float f)
{
/*...*/
}
}
;
message
_
handler ok2;
\begin{lstlisting}
// some place later
message
_
handler wrong = (
ok2.assign(
[](int i)
{
/*...*/
}
,
[](int i)
{
/*...*/
}
,
[](float f)
{
/*...*/
}
[](float f)
{
/*...*/
}
);
);
\end{lstlisting}
\end{lstlisting}
\end{itemize}
The correct way to initialize message handlers and behaviors is to either
use the constructor or the member function
\lstinline
^
assign
^
:
\begin{lstlisting}
message
_
handler ok1
{
[](int i)
{
/*...*/
}
,
[](float f)
{
/*...*/
}
}
;
message
_
handler ok2;
// some place later
ok2.assign(
[](int i)
{
/*...*/
}
,
[](float f)
{
/*...*/
}
);
\end{lstlisting}
\subsection
{
Event-Based API
}
\subsection
{
Event-Based API
}
\begin{itemize}
The member function
\lstinline
^
become
^
does not block, i.e., always returns
\item
The member function
\lstinline
^
become
^
does not block, i.e., always
immediately. Thus, lambda expressions should
\textit
{
always
}
capture by value.
returns immediately. Thus, lambda expressions should
\textit
{
always
}
capture
Otherwise, all references on the stack will cause undefined behavior if the
by value. Otherwise, all references on the stack will cause undefined
lambda expression is executed.
behavior if the lambda expression is executed.
\end{itemize}
\subsection
{
Requests
}
\subsection
{
Requests
}
\begin{itemize}
A handle returned by
\lstinline
^
request
^
represents
\emph
{
exactly one
}
response
\item
A handle returned by
\lstinline
^
request
^
represents
\emph
{
exactly one
}
message. It is not possible to receive more than one response message.
response message. It is not possible to receive more than one response
message.
\item
The handle returned by
\lstinline
^
request
^
is bound to the calling actor.
It is not possible to transfer a handle to a response to another actor.
\end{itemize}
\clearpage
The handle returned by
\lstinline
^
request
^
is bound to the calling actor. It is
not possible to transfer a handle to a response to another actor.
\clearpage
\subsection
{
Sharing
}
\subsection
{
Sharing
}
\begin{itemize}
It is strongly recommended to
\textbf
{
not
}
share states between actors. In
\item
It is strongly recommended to
\textbf
{
not
}
share states between actors.
particular, no actor shall ever access member variables or member functions of
In particular, no actor shall ever access member variables or member
another actor. Accessing shared memory segments concurrently can cause undefined
functions of another actor. Accessing shared memory segments concurrently
behavior that is incredibly hard to find and debug. However, sharing
can cause undefined behavior that is incredibly hard to find and debug.
\textit
{
data
}
between actors is fine, as long as the data is
\textit
{
immutable
}
However, sharing
\textit
{
data
}
between actors is fine, as long as the data
and its lifetime is guaranteed to outlive all actors. The simplest way to meet
is
\textit
{
immutable
}
and its lifetime is guaranteed to outlive all actors.
the lifetime guarantee is by storing the data in smart pointers such as
The simplest way to meet the lifetime guarantee is by storing the data in
\lstinline
^
std::shared
_
ptr
^
. Nevertheless, the recommended way of sharing
smart pointers such as
\lstinline
^
std::shared
_
ptr
^
. Nevertheless, the
informations is message passing. Sending the same message to multiple actors
recommended way of sharing informations is message passing. Sending the
does not result in copying the data several times.
same message to multiple actors does not result in copying the data several
times.
\end{itemize}
doc/tex/Introduction.tex
View file @
d15392b5
...
@@ -98,7 +98,7 @@ knowing the proper handle type. Pointers must be converted to a handle via
...
@@ -98,7 +98,7 @@ knowing the proper handle type. Pointers must be converted to a handle via
\subsubsection
{
Spawning
}
\subsubsection
{
Spawning
}
``Spawning''
an actor means to create and run a new actor.
\emph
{
Spawning
}
an actor means to create and run a new actor.
\subsubsection
{
Monitor
}
\subsubsection
{
Monitor
}
\label
{
monitor
}
\label
{
monitor
}
...
...
doc/tex/MessagePassing.tex
View file @
d15392b5
...
@@ -153,13 +153,15 @@ When passing the \lstinline^cells^ vector to our three different
...
@@ -153,13 +153,15 @@ When passing the \lstinline^cells^ vector to our three different
implementations, we observe three outputs. Our
\lstinline
^
waiting
_
testee
^
actor
implementations, we observe three outputs. Our
\lstinline
^
waiting
_
testee
^
actor
will always print:
will always print:
{
\footnotesize
\begin{verbatim}
\begin{footnotesize}
\begin{verbatim}
cell #9 -> 16
cell #9 -> 16
cell #8 -> 9
cell #8 -> 9
cell #7 -> 4
cell #7 -> 4
cell #6 -> 1
cell #6 -> 1
cell #5 -> 0
cell #5 -> 0
\end{verbatim}
}
\end{verbatim}
\end{footnotesize}
This is because
\lstinline
^
await
^
puts the one-shots handlers onto a stack and
This is because
\lstinline
^
await
^
puts the one-shots handlers onto a stack and
enforces LIFO order by re-ordering incoming response messages.
enforces LIFO order by re-ordering incoming response messages.
...
@@ -170,13 +172,15 @@ immediately.
...
@@ -170,13 +172,15 @@ immediately.
Finally, the
\lstinline
^
blocking
_
testee
^
implementation will always print:
Finally, the
\lstinline
^
blocking
_
testee
^
implementation will always print:
{
\footnotesize
\begin{verbatim}
\begin{footnotesize}
\begin{verbatim}
cell #5 -> 0
cell #5 -> 0
cell #6 -> 1
cell #6 -> 1
cell #7 -> 4
cell #7 -> 4
cell #8 -> 9
cell #8 -> 9
cell #9 -> 16
cell #9 -> 16
\end{verbatim}
}
\end{verbatim}
\end{footnotesize}
Both event-based approaches send all requests, install a series of one-shot
Both event-based approaches send all requests, install a series of one-shot
handlers, and then return from the implementing function. In contrast, the
handlers, and then return from the implementing function. In contrast, the
...
...
doc/tex/ReferenceCounting.tex
View file @
d15392b5
...
@@ -103,7 +103,7 @@ because the actor objects themselves can get destroyed independently from their
...
@@ -103,7 +103,7 @@ because the actor objects themselves can get destroyed independently from their
control block. A weak reference is only formed by
\lstinline
^
actor
_
addr
^
control block. A weak reference is only formed by
\lstinline
^
actor
_
addr
^
\see
{
actor-address
}
.
\see
{
actor-address
}
.
\subsection
{
Converting Actor References with
\
texttt
{
actor
\_
cast
}
}
\subsection
{
Converting Actor References with
\
lstinline
^
actor
_
cast
^
}
\label
{
actor-cast
}
\label
{
actor-cast
}
The function
\lstinline
^
actor
_
cast
^
converts between actor pointers and
The function
\lstinline
^
actor
_
cast
^
converts between actor pointers and
...
...
libcaf_core/caf/detail/variant_data.hpp
View file @
d15392b5
...
@@ -39,22 +39,48 @@
...
@@ -39,22 +39,48 @@
namespace
caf
{
namespace
caf
{
namespace
detail
{
namespace
detail
{
template
<
class
T0
,
typename
T1
=
unit_t
,
typename
T2
=
unit_t
,
template
<
class
T0
=
unit_t
,
class
T1
=
unit_t
,
class
T2
=
unit_t
,
typename
T3
=
unit_t
,
typename
T4
=
unit_t
,
typename
T5
=
unit_t
,
class
T3
=
unit_t
,
class
T4
=
unit_t
,
class
T5
=
unit_t
,
typename
T6
=
unit_t
,
typename
T7
=
unit_t
,
typename
T8
=
unit_t
,
class
T6
=
unit_t
,
class
T7
=
unit_t
,
class
T8
=
unit_t
,
typename
T9
=
unit_t
,
typename
T10
=
unit_t
,
typename
T11
=
unit_t
,
class
T9
=
unit_t
,
class
T10
=
unit_t
,
class
T11
=
unit_t
,
typename
T12
=
unit_t
,
typename
T13
=
unit_t
,
typename
T14
=
unit_t
,
class
T12
=
unit_t
,
class
T13
=
unit_t
,
class
T14
=
unit_t
,
typename
T15
=
unit_t
,
typename
T16
=
unit_t
,
typename
T17
=
unit_t
,
class
T15
=
unit_t
,
class
T16
=
unit_t
,
class
T17
=
unit_t
,
typename
T18
=
unit_t
,
typename
T19
=
unit_t
,
typename
T20
=
unit_t
>
class
T18
=
unit_t
,
class
T19
=
unit_t
,
class
T20
=
unit_t
,
class
T21
=
unit_t
,
class
T22
=
unit_t
,
class
T23
=
unit_t
,
class
T24
=
unit_t
,
class
T25
=
unit_t
,
class
T26
=
unit_t
,
class
T27
=
unit_t
,
class
T28
=
unit_t
,
class
T29
=
unit_t
>
struct
variant_data
{
struct
variant_data
{
union
{
union
{
T0
v0
;
T1
v1
;
T2
v2
;
T0
v0
;
T3
v3
;
T4
v4
;
T5
v5
;
T1
v1
;
T6
v6
;
T7
v7
;
T8
v8
;
T2
v2
;
T9
v9
;
T10
v10
;
T11
v11
;
T3
v3
;
T12
v12
;
T13
v13
;
T14
v14
;
T4
v4
;
T15
v15
;
T16
v16
;
T17
v17
;
T5
v5
;
T18
v18
;
T19
v19
;
T20
v20
;
T6
v6
;
T7
v7
;
T8
v8
;
T9
v9
;
T10
v10
;
T11
v11
;
T12
v12
;
T13
v13
;
T14
v14
;
T15
v15
;
T16
v16
;
T17
v17
;
T18
v18
;
T19
v19
;
T20
v20
;
T21
v21
;
T22
v22
;
T23
v23
;
T24
v24
;
T25
v25
;
T26
v26
;
T27
v27
;
T28
v28
;
T29
v29
;
};
};
variant_data
()
{
variant_data
()
{
...
@@ -86,6 +112,15 @@ struct variant_data {
...
@@ -86,6 +112,15 @@ struct variant_data {
CAF_VARIANT_DATA_GETTER
(
18
)
CAF_VARIANT_DATA_GETTER
(
18
)
CAF_VARIANT_DATA_GETTER
(
19
)
CAF_VARIANT_DATA_GETTER
(
19
)
CAF_VARIANT_DATA_GETTER
(
20
)
CAF_VARIANT_DATA_GETTER
(
20
)
CAF_VARIANT_DATA_GETTER
(
21
)
CAF_VARIANT_DATA_GETTER
(
22
)
CAF_VARIANT_DATA_GETTER
(
23
)
CAF_VARIANT_DATA_GETTER
(
24
)
CAF_VARIANT_DATA_GETTER
(
25
)
CAF_VARIANT_DATA_GETTER
(
26
)
CAF_VARIANT_DATA_GETTER
(
27
)
CAF_VARIANT_DATA_GETTER
(
28
)
CAF_VARIANT_DATA_GETTER
(
29
)
};
};
struct
variant_data_destructor
{
struct
variant_data_destructor
{
...
...
libcaf_core/caf/variant.hpp
View file @
d15392b5
...
@@ -146,7 +146,7 @@ public:
...
@@ -146,7 +146,7 @@ public:
// -- sanity checks ----------------------------------------------------------
// -- sanity checks ----------------------------------------------------------
static_assert
(
sizeof
...(
Ts
)
<=
2
0
,
"Too many template arguments given."
);
static_assert
(
sizeof
...(
Ts
)
<=
3
0
,
"Too many template arguments given."
);
static_assert
(
sizeof
...(
Ts
)
>
0
,
"No template argument given."
);
static_assert
(
sizeof
...(
Ts
)
>
0
,
"No template argument given."
);
...
@@ -284,6 +284,16 @@ public:
...
@@ -284,6 +284,16 @@ public:
CAF_VARIANT_CASE
(
17
);
CAF_VARIANT_CASE
(
17
);
CAF_VARIANT_CASE
(
18
);
CAF_VARIANT_CASE
(
18
);
CAF_VARIANT_CASE
(
19
);
CAF_VARIANT_CASE
(
19
);
CAF_VARIANT_CASE
(
20
);
CAF_VARIANT_CASE
(
21
);
CAF_VARIANT_CASE
(
22
);
CAF_VARIANT_CASE
(
23
);
CAF_VARIANT_CASE
(
24
);
CAF_VARIANT_CASE
(
25
);
CAF_VARIANT_CASE
(
26
);
CAF_VARIANT_CASE
(
27
);
CAF_VARIANT_CASE
(
28
);
CAF_VARIANT_CASE
(
29
);
}
}
}
}
...
@@ -481,6 +491,16 @@ inspect(Inspector& f, variant_writer<variant<Ts...>>& x) {
...
@@ -481,6 +491,16 @@ inspect(Inspector& f, variant_writer<variant<Ts...>>& x) {
CAF_VARIANT_ASSIGN_CASE
(
17
);
CAF_VARIANT_ASSIGN_CASE
(
17
);
CAF_VARIANT_ASSIGN_CASE
(
18
);
CAF_VARIANT_ASSIGN_CASE
(
18
);
CAF_VARIANT_ASSIGN_CASE
(
19
);
CAF_VARIANT_ASSIGN_CASE
(
19
);
CAF_VARIANT_ASSIGN_CASE
(
20
);
CAF_VARIANT_ASSIGN_CASE
(
21
);
CAF_VARIANT_ASSIGN_CASE
(
22
);
CAF_VARIANT_ASSIGN_CASE
(
23
);
CAF_VARIANT_ASSIGN_CASE
(
24
);
CAF_VARIANT_ASSIGN_CASE
(
25
);
CAF_VARIANT_ASSIGN_CASE
(
26
);
CAF_VARIANT_ASSIGN_CASE
(
27
);
CAF_VARIANT_ASSIGN_CASE
(
28
);
CAF_VARIANT_ASSIGN_CASE
(
29
);
}
}
}
}
...
...
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