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
0ddf1f29
Commit
0ddf1f29
authored
Jun 10, 2016
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Get core functionality running
parent
8d555940
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
288 additions
and
83 deletions
+288
-83
src/main.cpp
src/main.cpp
+288
-83
No files found.
src/main.cpp
View file @
0ddf1f29
...
...
@@ -34,24 +34,79 @@ CAF_POP_WARNINGS
#include "caf/all.hpp"
#include "caf/io/all.hpp"
static_assert
(
PY_MAJOR_VERSION
>
3
||
(
PY_MAJOR_VERSION
==
3
&&
PY_MINOR_VERSION
>=
5
),
"CAF requires Python >= 3.5"
)
;
using
std
::
cout
;
using
std
::
cerr
;
using
std
::
endl
;
using
namespace
caf
;
using
namespace
std
;
namespace
{
constexpr
char
default_banner
[]
=
R"__(
_________ _____ __ __
/ ____/ | / ___// / / /
/ / / /| | \__ \/ /_/ / CAF
/ /___/ ___ |___/ / __ / Shell
\____/_/ |_/____/_/ /_/
____ __ __
_________ / __/ ____ __ __/ /_/ /_ ____ ____
/ ___/ __ `/ /_____/ __ \/ / / / __/ __ \/ __ \/ __ `
/ /__/ /_/ / __/___/ /_/ / /_/ / /_/ / / / /_/ / / / /
\___/\__,_/_/ / .___/\__, /\__/_/ /_/\____/_/ /_/
/_/ /____/
)__"
;
namespace
py
=
pybind11
;
constexpr
char
init_script
[]
=
R"__(
from CAF import *
caf_mail_cache=[]
def select_from_mail_cache(msg_filter):
global caf_mail_cache
for i, v in enumerate(caf_mail_cache):
if msg_filter(v):
return caf_mail_cache.pop(i)
def no_receive_filter(x):
return True
def receive_one(abs_timeout):
if abs_timeout:
return dequeue_message_with_timeout(abs_timeout)
else:
return dequeue_message()
def receive(timeout = None, msg_filter = no_receive_filter):
# try to get an element from the mailbox for predicate
msg = select_from_mail_cache(msg_filter)
if msg:
return msg
# calculate absolute timeout
abs_timeout = None
if timeout:
abs_timeout = absolute_receive_timeout(int(timeout))
# receive message via mailbox
msg = receive_one(abs_timeout)
while msg and not msg_filter(msg):
caf_mail_cache.append(msg)
msg = receive_one(abs_timeout)
return msg
namespace
{
)__"
;
}
// namespace <anonymous>
namespace
caf
{
void
register_class
(
atom_value
*
,
pybind11
::
module
&
m
,
const
std
::
string
&
name
)
{
auto
repr_fun
=
[](
atom_value
x
)
{
return
"atom('"
+
to_string
(
x
)
+
"')"
;
};
auto
cmp
=
[](
atom_value
x
,
atom_value
y
)
{
return
x
==
y
;
};
pybind11
::
class_
<
atom_value
>
(
m
,
name
.
c_str
())
.
def
(
"__str__"
,
(
std
::
string
(
*
)(
const
atom_value
&
))
&
to_string
)
.
def
(
"__repr__"
,
repr_fun
)
.
def
(
"__eq__"
,
cmp
);
}
namespace
python
{
class
binding
{
public:
...
...
@@ -81,7 +136,7 @@ public:
return
builtin_
;
}
virtual
void
append
(
message_builder
&
xs
,
py
::
handle
x
)
const
=
0
;
virtual
void
append
(
message_builder
&
xs
,
py
bind11
::
handle
x
)
const
=
0
;
private:
std
::
string
python_name_
;
...
...
@@ -101,8 +156,7 @@ class default_py_binding : public py_binding {
public:
using
py_binding
::
py_binding
;
void
append
(
message_builder
&
xs
,
py
::
handle
x
)
const
override
{
//xs.append(*reinterpret_cast<T*>(x.cast<void*>()));
void
append
(
message_builder
&
xs
,
pybind11
::
handle
x
)
const
override
{
xs
.
append
(
x
.
cast
<
T
>
());
}
};
...
...
@@ -111,68 +165,138 @@ class cpp_binding : public binding {
public:
using
binding
::
binding
;
virtual
void
register_type
(
pybind11
::
module
&
target
)
const
=
0
;
virtual
pybind11
::
object
to_object
(
message
&
xs
,
size_t
pos
)
const
=
0
;
};
template
<
class
T
>
class
default_cpp_binding
:
public
cpp_binding
{
public:
using
cpp_binding
::
cpp_binding
;
void
append
(
message_builder
&
xs
,
pybind11
::
handle
x
)
const
override
{
xs
.
append
(
x
.
cast
<
T
>
());
}
virtual
py
::
object
to_object
(
message
&
xs
,
size_t
pos
)
const
=
0
;
pybind11
::
object
to_object
(
message
&
xs
,
size_t
pos
)
const
override
{
return
pybind11
::
cast
(
xs
.
get_as
<
T
>
(
pos
));
}
};
using
binding_ptr
=
std
::
unique_ptr
<
binding
>
;
using
py_binding_ptr
=
std
::
unique_ptr
<
py_binding
>
;
using
cpp_binding_ptr
=
std
::
unique_ptr
<
cpp_binding
>
;
atom_value
atom_from_string
(
const
std
::
string
&
str
)
{
static
constexpr
size_t
buf_size
=
11
;
char
buf
[
buf_size
];
memset
(
buf
,
0
,
buf_size
);
strncpy
(
buf
,
str
.
c_str
(),
std
::
min
<
size_t
>
(
buf_size
-
1
,
str
.
size
()));
return
atom
(
buf
);
}
template
<
class
T
>
std
::
string
py_repr
(
const
T
&
x
)
{
return
deep_to_string
(
x
);
class
has_register_class
{
private:
template
<
class
U
>
static
auto
test
(
U
*
x
)
->
decltype
(
register_class
(
x
,
std
::
declval
<
pybind11
::
module
&>
(),
std
::
declval
<
const
std
::
string
&>
()));
static
auto
test
(...)
->
std
::
false_type
;
using
type
=
decltype
(
test
(
static_cast
<
T
*>
(
nullptr
)));
public:
static
constexpr
bool
value
=
std
::
is_same
<
type
,
void
>::
value
;
};
template
<
class
T
>
class
has_to_string
{
private:
template
<
class
U
>
static
auto
test
(
U
*
x
)
->
decltype
(
to_string
(
*
x
));
static
auto
test
(...)
->
void
;
using
type
=
decltype
(
test
(
static_cast
<
T
*>
(
nullptr
)));
public:
static
constexpr
bool
value
=
std
::
is_same
<
type
,
std
::
string
>::
value
;
};
template
<
class
T
>
typename
std
::
enable_if
<
!
has_register_class
<
T
>::
value
&&
has_to_string
<
T
>::
value
>::
type
default_python_class_init
(
pybind11
::
module
&
m
,
const
std
::
string
&
name
)
{
auto
str_impl
=
[](
const
T
&
x
)
{
return
to_string
(
x
);
};
pybind11
::
class_
<
T
>
(
m
,
name
.
c_str
())
.
def
(
"__str__"
,
str_impl
);
}
std
::
string
py_repr
(
const
atom_value
&
x
)
{
return
"<atom:'"
+
to_string
(
x
)
+
"'>"
;
template
<
class
T
>
typename
std
::
enable_if
<
!
has_register_class
<
T
>::
value
&&
!
has_to_string
<
T
>::
value
>::
type
default_python_class_init
(
pybind11
::
module
&
m
,
const
std
::
string
&
name
)
{
auto
str_impl
=
[](
const
T
&
x
)
{
return
to_string
(
x
);
};
pybind11
::
class_
<
T
>
(
m
,
name
.
c_str
());
}
template
<
class
T
>
class
default_cpp_binding
:
public
cpp_binding
{
typename
std
::
enable_if
<
has_register_class
<
T
>::
value
>::
type
default_python_class_init
(
pybind11
::
module
&
m
,
const
std
::
string
&
name
)
{
register_class
(
static_cast
<
T
*>
(
nullptr
),
m
,
name
);
}
struct
absolute_receive_timeout
{
public:
using
cpp_binding
::
cpp_binding
;
using
ms
=
std
::
chrono
::
milliseconds
;
using
clock_type
=
std
::
chrono
::
high_resolution_clock
;
void
append
(
message_builder
&
xs
,
py
::
handle
x
)
const
override
{
/*
auto typeinfo = py::detail::get_type_info(typeid(T));
CAF_ASSERT(PyType_IsSubtype(Py_TYPE(x.ptr()), typeinfo->type));
auto vptr = ((py::detail::instance<void>*) x.ptr())->value;
xs.append(*reinterpret_cast<T*>(vptr));
*/
xs
.
append
(
x
.
cast
<
T
>
());
absolute_receive_timeout
(
int
msec
)
{
x_
=
clock_type
::
now
()
+
ms
(
msec
);
}
void
register_type
(
pybind11
::
module
&
target
)
const
override
{
py
::
class_
<
T
>
(
target
,
this
->
python_name
().
c_str
())
.
def
(
"__str__"
,
&
default_cpp_binding
::
str_impl
,
this
->
docstring
().
c_str
())
.
def
(
"__repr__"
,
&
default_cpp_binding
::
repr_impl
,
this
->
docstring
().
c_str
());
}
absolute_receive_timeout
()
=
default
;
absolute_receive_timeout
(
const
absolute_receive_timeout
&
)
=
default
;
absolute_receive_timeout
&
operator
=
(
const
absolute_receive_timeout
&
)
=
default
;
py
::
object
to_object
(
message
&
xs
,
size_t
pos
)
const
override
{
return
py
::
object
(
py
::
detail
::
type_caster
<
typename
py
::
detail
::
intrinsic_type
<
T
>::
type
>::
cast
(
xs
.
get_as
<
T
>
(
pos
),
py
::
return_value_policy
::
automatic_reference
,
nullptr
),
false
);
const
clock_type
::
time_point
&
value
()
const
{
return
x_
;
}
private:
static
std
::
string
str_impl
(
T
*
ptr
)
{
return
deep_to_string
(
*
ptr
);
friend
void
serialize
(
serializer
&
sink
,
absolute_receive_timeout
&
x
,
const
unsigned
int
)
{
auto
tse
=
x
.
x_
.
time_since_epoch
();
uint64_t
ms_since_epoch
=
std
::
chrono
::
duration_cast
<
ms
>
(
tse
).
count
();
sink
<<
ms_since_epoch
;
}
static
std
::
string
repr_impl
(
T
*
ptr
)
{
return
py_repr
(
*
ptr
);
friend
void
serialize
(
deserializer
&
source
,
absolute_receive_timeout
&
x
,
const
unsigned
int
)
{
uint64_t
ms_since_epoch
;
source
>>
ms_since_epoch
;
clock_type
::
time_point
y
;
y
+=
ms
(
static_cast
<
ms
::
rep
>
(
ms_since_epoch
));
x
.
x_
=
y
;
}
};
using
binding_ptr
=
std
::
unique_ptr
<
binding
>
;
using
py_binding_ptr
=
std
::
unique_ptr
<
py_binding
>
;
using
cpp_binding_ptr
=
std
::
unique_ptr
<
cpp_binding
>
;
private:
clock_type
::
time_point
x_
;
}
;
atom_value
atom_from_string
(
const
std
::
string
&
str
)
{
char
buf
[
11
];
strncpy
(
buf
,
str
.
c_str
(),
std
::
min
<
size_t
>
(
10
,
str
.
size
()));
buf
[
10
]
=
'\0'
;
return
atom
(
buf
);
void
register_class
(
absolute_receive_timeout
*
,
pybind11
::
module
&
m
,
const
std
::
string
&
name
)
{
pybind11
::
class_
<
absolute_receive_timeout
>
(
m
,
name
.
c_str
())
.
def
(
pybind11
::
init
<>
())
.
def
(
pybind11
::
init
<
int
>
()
);
}
class
py_config
:
public
actor_system_config
{
...
...
@@ -180,6 +304,8 @@ public:
std
::
string
pre_run
;
std
::
string
banner
=
default_banner
;
using
register_fun
=
std
::
function
<
void
(
pybind11
::
module
&
,
const
std
::
string
&
)
>
;
py_config
()
{
// allow CAF to convert native Python types to C++ types
add_py
<
int
>
(
"int"
);
...
...
@@ -191,28 +317,37 @@ public:
add_cpp
<
message
>
(
"message"
,
"@message"
);
add_cpp
<
atom_value
>
(
"atom_value"
,
"@atom"
);
// fill list for native type bindings
add_cpp
<
bool
>
(
"bool"
,
"bool"
,
true
);
add_cpp
<
float
>
(
"float"
,
"float"
,
true
);
add_cpp
<
int32_t
>
(
"int32_t"
,
"@i32"
,
true
);
add_cpp
<
std
::
string
>
(
"str"
,
"@str"
,
true
);
add_cpp
<
bool
>
(
"bool"
,
"bool"
,
nullptr
);
add_cpp
<
float
>
(
"float"
,
"float"
,
nullptr
);
add_cpp
<
int32_t
>
(
"int32_t"
,
"@i32"
,
nullptr
);
add_cpp
<
std
::
string
>
(
"str"
,
"@str"
,
nullptr
);
// custom types of caf_python
add_message_type
<
absolute_receive_timeout
>
(
"absolute_receive_timeout"
);
}
template
<
class
T
>
actor_system_config
&
add_message_type
(
std
::
string
name
)
{
add_cpp
<
T
>
(
name
,
name
);
py_config
&
add_message_type
(
std
::
string
name
,
register_fun
reg
=
&
default_python_class_init
<
T
>
)
{
add_cpp
<
T
>
(
name
,
name
,
std
::
move
(
reg
));
actor_system_config
::
add_message_type
<
T
>
(
std
::
move
(
name
));
return
*
this
;
}
void
py_init
(
pybind11
::
module
&
x
)
const
{
for
(
auto
&
f
:
register_funs_
)
f
(
x
);
}
void
py_init
(
py
::
module
&
x
)
const
{
for
(
auto
&
kvp
:
cpp_bindings_
)
if
(
!
kvp
.
second
->
builtin
())
kvp
.
second
->
register_type
(
x
);
std
::
string
full_pre_run_script
()
const
{
return
init_script
+
pre_run
;
}
std
::
string
ipython_script
()
const
{
// prepare preload script by formatting it with <space><space>'...'
std
::
vector
<
std
::
string
>
lines
;
split
(
lines
,
pre_run
,
is_any_of
(
"
\n
"
),
token_compress_on
);
auto
full_pre_run
=
full_pre_run_script
();
split
(
lines
,
full_pre_run
,
is_any_of
(
"
\n
"
),
token_compress_on
);
for
(
auto
&
line
:
lines
)
{
line
.
insert
(
0
,
" '"
);
line
+=
"'"
;
...
...
@@ -221,7 +356,10 @@ public:
oss
<<
"import IPython"
<<
endl
<<
"c = IPython.Config()"
<<
endl
<<
"c.InteractiveShellApp.exec_lines = ["
<<
endl
<<
join
(
lines
,
"
\n
"
)
<<
endl
<<
"
\"\"\"
"
<<
full_pre_run
<<
"
\"\"\"
"
<<
endl
//<< join(lines, ",\n") << endl
<<
"]"
<<
endl
<<
"c.PromptManager.in_template = ' $: '"
<<
endl
<<
"c.PromptManager.in2_template = ' -> '"
<<
endl
...
...
@@ -255,8 +393,11 @@ private:
}
template
<
class
T
>
void
add_cpp
(
std
::
string
py_name
,
std
::
string
cpp_name
,
bool
builtin
=
false
)
{
auto
ptr
=
new
default_cpp_binding
<
T
>
(
py_name
,
builtin
);
void
add_cpp
(
std
::
string
py_name
,
std
::
string
cpp_name
,
register_fun
reg
=
&
default_python_class_init
<
T
>
)
{
if
(
reg
)
register_funs_
.
push_back
([
=
](
pybind11
::
module
&
m
)
{
reg
(
m
,
py_name
);
});
auto
ptr
=
new
default_cpp_binding
<
T
>
(
py_name
,
reg
!=
nullptr
);
// all type names are prefix with "CAF."
py_name
.
insert
(
0
,
"CAF."
);
cpp_bindings_
.
emplace
(
py_name
,
cpp_binding_ptr
{
ptr
});
...
...
@@ -264,13 +405,18 @@ private:
portable_bindings_
.
emplace
(
std
::
move
(
cpp_name
),
ptr
);
}
std
::
unordered_map
<
std
::
string
,
cpp_binding
*>
portable_bindings_
;
template
<
class
T
>
void
add_cpp
(
std
::
string
name
)
{
add_cpp
<
T
>
(
name
,
name
);
}
std
::
unordered_map
<
std
::
string
,
cpp_binding
*>
portable_bindings_
;
std
::
unordered_map
<
std
::
string
,
binding
*>
bindings_
;
std
::
unordered_map
<
std
::
string
,
cpp_binding_ptr
>
cpp_bindings_
;
std
::
unordered_map
<
std
::
string
,
py_binding_ptr
>
py_bindings_
;
};
std
::
vector
<
std
::
function
<
void
(
pybind11
::
module
&
)
>>
register_funs_
;
};
struct
py_context
{
const
py_config
&
cfg
;
actor_system
&
system
;
...
...
@@ -296,7 +442,7 @@ void set_py_exception(Ts&&... xs) {
PyErr_SetString
(
PyExc_RuntimeError
,
oss
.
str
().
c_str
());
}
void
py_send
(
py
::
args
xs
)
{
void
py_send
(
py
bind11
::
args
xs
)
{
if
(
xs
.
size
()
<
2
)
{
set_py_exception
(
"Too few arguments to call CAF.send"
);
return
;
...
...
@@ -319,13 +465,10 @@ void py_send(py::args xs) {
s_context
->
self
->
send
(
dest
,
mb
.
move_to_message
());
}
py
::
tuple
py_dequeue
(
)
{
py
bind11
::
tuple
tuple_from_message
(
message
msg
)
{
auto
&
self
=
s_context
->
self
;
self
->
await_data
();
auto
ptr
=
self
->
next_message
();
auto
&
msg
=
ptr
->
msg
;
auto
&
bindings
=
s_context
->
cfg
.
portable_bindings
();
py
::
tuple
result
(
msg
.
size
());
py
bind11
::
tuple
result
(
msg
.
size
());
for
(
size_t
i
=
0
;
i
<
msg
.
size
();
++
i
)
{
auto
rtti
=
msg
.
type
(
i
);
auto
str_ptr
=
self
->
system
().
types
().
portable_name
(
rtti
);
...
...
@@ -346,34 +489,93 @@ py::tuple py_dequeue() {
return
result
;
}
pybind11
::
tuple
py_dequeue
()
{
auto
&
self
=
s_context
->
self
;
auto
ptr
=
self
->
next_message
();
while
(
!
ptr
)
{
self
->
await_data
();
ptr
=
self
->
next_message
();
}
return
tuple_from_message
(
std
::
move
(
ptr
->
msg
));
}
pybind11
::
tuple
py_dequeue_with_timeout
(
absolute_receive_timeout
timeout
)
{
auto
&
self
=
s_context
->
self
;
auto
ptr
=
self
->
next_message
();
while
(
!
ptr
)
{
if
(
!
self
->
await_data
(
timeout
.
value
()))
return
pybind11
::
none
{};
ptr
=
self
->
next_message
();
}
return
tuple_from_message
(
std
::
move
(
ptr
->
msg
));
}
actor
py_self
()
{
return
s_context
->
self
;
}
struct
foo
{
int
x
;
int
y
;
foo
()
:
x
(
0
),
y
(
0
)
{
// nop
}
foo
(
int
a
,
int
b
)
:
x
(
a
),
y
(
b
)
{
// nop
}
};
template
<
class
Processor
>
void
serialize
(
Processor
&
proc
,
foo
&
f
,
const
unsigned
int
)
{
proc
&
f
.
x
;
proc
&
f
.
y
;
}
std
::
string
to_string
(
const
foo
&
x
)
{
return
"foo"
+
deep_to_string_as_tuple
(
x
.
x
,
x
.
y
);
}
void
register_class
(
foo
*
,
pybind11
::
module
&
m
,
const
std
::
string
&
name
)
{
pybind11
::
class_
<
foo
>
(
m
,
name
.
c_str
())
.
def
(
pybind11
::
init
<>
())
.
def
(
pybind11
::
init
<
int
,
int
>
())
.
def
(
"__str__"
,
(
std
::
string
(
*
)(
const
foo
&
))
&
to_string
)
.
def
(
"__repr__"
,
(
std
::
string
(
*
)(
const
foo
&
))
&
to_string
)
.
def_readwrite
(
"x"
,
&
foo
::
x
)
.
def_readwrite
(
"y"
,
&
foo
::
y
);
}
class
config
:
public
py_config
{
public:
std
::
string
py_file
;
config
()
{
pre_run
=
"from CAF import *"
;
opt_group
{
custom_options_
,
"
global
"
}
add_message_type
<
foo
>
(
"foo"
)
;
opt_group
{
custom_options_
,
"
python
"
}
.
add
(
py_file
,
"file,f"
,
"Run script instead of interactive shell."
);
}
};
PyObject
*
caf_module_init
()
{
py
::
module
m
(
"CAF"
,
"Python binding for CAF"
);
py
bind11
::
module
m
(
"CAF"
,
"Python binding for CAF"
);
s_context
->
cfg
.
py_init
(
m
);
// add classes
// add free functions
m
.
def
(
"send"
,
&
py_send
,
"Sends a message to an actor"
)
.
def
(
"dequeue_message"
,
&
py_dequeue
,
"Receives the next message"
)
.
def
(
"dequeue_message_with_timeout"
,
&
py_dequeue_with_timeout
,
"Receives the next message"
)
.
def
(
"self"
,
&
py_self
,
"Returns the global self handle"
)
.
def
(
"atom"
,
&
atom_from_string
,
"Creates an atom from a string"
);
return
m
.
ptr
();
}
}
// namespace <anonymous>
}
// namespace python
}
// namespace caf
namespace
{
using
namespace
caf
;
using
namespace
caf
::
python
;
void
caf_main
(
actor_system
&
system
,
const
config
&
cfg
)
{
// register system and scoped actor in global variables
...
...
@@ -392,7 +594,8 @@ void caf_main(actor_system& system, const config& cfg) {
Py_Finalize
();
return
;
}
py_res
=
PyRun_SimpleString
(
cfg
.
pre_run
.
c_str
());
auto
full_pre_run
=
cfg
.
full_pre_run_script
();
py_res
=
PyRun_SimpleString
(
full_pre_run
.
c_str
());
if
(
py_res
==
0
)
py_res
=
PyRun_SimpleFileEx
(
fp
,
cfg
.
py_file
.
c_str
(),
1
);
}
else
{
...
...
@@ -406,4 +609,6 @@ void caf_main(actor_system& system, const config& cfg) {
Py_Finalize
();
}
}
// namespace <anonymous>
CAF_MAIN
()
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