Commit c067ea95 authored by Dominik Charousset's avatar Dominik Charousset

Change license to BSD and adopt new coding style

parent adf89af5
---
AccessModifierOffset: -1
AlignEscapedNewlinesLeft: false
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackParameters: true
BreakBeforeBinaryOperators: false
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: false
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
IndentCaseLabels: true
IndentFunctionDeclarationAfterType: false
IndentWidth: 2
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PointerBindsToType: true
SpaceBeforeAssignmentOperators: true
SpaceAfterControlStatementKeyword: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
Standard: Cpp11
UseTab: Never
BreakConstructorInitializersBeforeComma: false
...
This files specifies the coding style for CAF source files.
The style is based on the
[Google C++ Style Guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml).
# Example for the Impatient
#### `libcaf_example/caf/example/my_class.hpp`
```cpp
#ifndef CAF_EXAMPLE_MY_CLASS_HPP
#define CAF_EXAMPLE_MY_CLASS_HPP
#include <string>
namespace caf {
namespace example {
class my_class {
public:
/**
* @brief Constructs `my_class`.
*
* The class is only being used as style guide example.
*/
my_class();
/**
* @brief Destructs `my_class`.
*/
~my_class();
/**
* @brief Gets the name of this instance.
* Some more brief description.
*
* Long description goes here.
*/
inline const std::string& name() const {
return m_name;
}
/**
* @brief Sets the name of this instance.
*/
inline void name(std::string new_name) {
m_name = std::move(new_name);
}
/**
* @brief Prints the name to `STDIN`.
*/
void print_name() const;
/**
* @brief Does something. Maybe.
*/
void do_something();
private:
std::string m_name;
};
} // namespace example
} // namespace caf
#endif // CAF_EXAMPLE_MY_CLASS_HPP
```
#### `libcaf_example/src/my_class.cpp`
```cpp
#include "caf/example/my_class.hpp"
#include <iostream>
namespace caf {
namespace example {
namespace {
constexpr const char default_name[] = "my object";
} // namespace <anonymous>
my_class::my_class() : m_name(default_name) {
// nop
}
my_class::~my_class() {
// nop
}
void my_class::print_name() const {
std::cout << name() << std::endl;
}
void my_class::do_something() {
if (name() == default_name) {
std::cout << "You didn't gave me a proper name, so I "
<< "refuse to do something."
<< std::endl;
} else {
std::cout << "You gave me the name " << name()
<< "... Do you really think I'm willing to do something "
"for you after insulting me like that?"
<< std::endl;
}
}
} // namespace example
} // namespace caf
```
# General rules
- Use 2 spaces per indentation level.
- The maximum number of characters per line is 80.
- Never use tabs.
- Vertical whitespaces separate functions and are not used inside functions:
use comments to document logical blocks.
- Header filenames end in `.hpp`, implementation files end in `.cpp`.
- Member variables use the prefix `m_`.
- Thread-local variables use the prefix `t_`.
- Never declare more than one variable per line.
- `*` and `&` bind to the *type*.
- Class names, constants, and function names are all-lowercase with underscores.
- Template parameter names use CamelCase.
- Namespaces do not increase the indentation level.
- Access modifiers, e.g. `public`, are indented one space.
- Use the order `public`, `protected`, and then `private`.
- Always use `auto` to declare a variable unless you cannot initialize it
immediately or if you actually want a type conversion. In the latter case,
provide a comment why this conversion is necessary.
- Never use unwrapped, manual resource management such as `new` and `delete`.
- Do not use `typedef`. The syntax provided by `using` is much cleaner.
- Use `typename` only when referring to dependent names.
- Keywords are always followed by a whitespace: `if (...)`, `template <...>`,
`while (...)`, etc.
- Always use `{}` for bodies of control structures such as `if` or `while`,
even for bodies consiting only of a single statement.
- Opening braces belong to the same line:
```cpp
if (my_condition) {
my_fun();
} else {
my_other_fun();
}
```
- Use standard order for readability: C standard libraries, C++ standard
libraries, other libraries, (your) CAF headers:
```cpp
#include <sys/types.h>
#include <vector>
#include "some/other/library.hpp"
#include "caf/example/myclass.hpp"
```
# Headers
- Each `.cpp` file has an associated `.hpp` file.
Exceptions to this rule are unit tests and `main.cpp` files.
- Each class has its own pair of header and implementation
files and the relative path is derived from its full name.
For example, the header file for `caf::example::my_class` of `libcaf_example`
is located at `libcaf_example/caf/example/my_class.hpp`.
Source files simply belong to the `src` folder of the component, e.g.,
`libcaf_example/src/my_class.cpp`.
- All header files should use `#define` guards to prevent multiple inclusion.
The symbol name is `<RELATIVE>_<PATH>_<TO>_<FILE>_HPP`.
- Do not `#include` when a forward declaration suffices.
- Each library component must provide a `fwd.hpp` header providing forward
declartations for all types used in the user API.
- Each library component must provide an `all.hpp` header including
all headers for the user API and the main page for the documentation.
- Use `inline` for small functions (rule of thumb: 10 lines or less).
## Template Metaprogramming
Despite its power, template metaprogramming came to the language pretty
much by accident. Templates were never meant to be used for compile-time
algorithms and type transformations. This is why C++ punishes metaprogramming
with an insane amount of syntax noise. In CAF, we make excessive use of
templates. To keep the code readable despite all the syntax noise, we have some
extra rules for formatting metaprogramming code.
- Brake `using name = ...` statements always directly after `=` if it
does not fit in one line.
- Consider the *semantics* of a metaprogramming function. For example,
`std::conditional` is an if-then-else construct. Hence, place the if-clause
on its own line and do the same for the two cases.
- Use one level of indentation per "open" template and place the closing `>`,
`>::type` or `>::value` on its own line. For example:
```cpp
using optional_result_type =
typename std::conditional<
std::is_same<result_type, void>::value,
bool,
optional<result_type>
>::type;
// think of it as the following (not valid C++):
auto optional_result_type =
conditional {
if (result_type == void)
then (bool)
else (optional<result_type>)
};
```
- Note that this is not necessary when simply defining a type alias.
When dealing with "ordinary" templates, indenting based on the position of
the opening `<` is ok too, e.g.:
```cpp
using response_handle_type = response_handle<Subtype, message,
ResponseHandleTag>;
```
# Miscellaneous
## Rvalue references
- Use rvalue references whenever it makes sense.
- Write move constructors and assignment operators if possible.
## Casting
- Never use C-style casts.
## Preprocessor Macros
- Use macros if and only if you can't get the same result by using inline
functions or proper constants.
- Macro names use the form `CAF_<COMPONENT>_<NAME>`.
Boost Software License - Version 1.0 - August 17th, 2003
Copyright (c) 2011-2014, Dominik Charousset
All rights reserved.
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
/******************************************************************************\
* This example illustrates how to use aout. *
\******************************************************************************/
\ ******************************************************************************/
#include <chrono>
#include <cstdlib>
......@@ -11,22 +11,22 @@ using namespace caf;
using std::endl;
int main() {
std::srand(static_cast<unsigned>(std::time(0)));
for (int i = 1; i <= 50; ++i) {
spawn<blocking_api>([i](blocking_actor* self) {
aout(self) << "Hi there! This is actor nr. "
<< i << "!" << endl;
std::chrono::milliseconds tout{std::rand() % 1000};
self->delayed_send(self, tout, atom("done"));
self->receive(others() >> [i, self] {
aout(self) << "Actor nr. "
<< i << " says goodbye!" << endl;
});
});
}
// wait until all other actors we've spawned are done
await_all_actors_done();
// done
shutdown();
return 0;
std::srand(static_cast<unsigned>(std::time(0)));
for (int i = 1; i <= 50; ++i) {
spawn<blocking_api>([i](blocking_actor* self) {
aout(self) << "Hi there! This is actor nr. "
<< i << "!" << endl;
std::chrono::milliseconds tout{std::rand() % 1000};
self->delayed_send(self, tout, atom("done"));
self->receive(others() >> [i, self] {
aout(self) << "Actor nr. "
<< i << " says goodbye!" << endl;
});
});
}
// wait until all other actors we've spawned are done
await_all_actors_done();
// done
shutdown();
return 0;
}
This diff is collapsed.
#include <string>
#include <iostream>
#include "caf/all.hpp"
using namespace std;
using namespace caf;
behavior mirror(event_based_actor* self) {
// return the (initial) actor behavior
return {
// a handler for messages containing a single string
// that replies with a string
[=](const string& what) -> string {
// prints "Hello World!" via aout (thread-safe cout wrapper)
aout(self) << what << endl;
// terminates this actor ('become' otherwise loops forever)
self->quit();
// reply "!dlroW olleH"
return string(what.rbegin(), what.rend());
}
};
// return the (initial) actor behavior
return {
// a handler for messages containing a single string
// that replies with a string
[=](const string& what) -> string {
// prints "Hello World!" via aout (thread-safe cout wrapper)
aout(self) << what << endl;
// terminates this actor ('become' otherwise loops forever)
self->quit();
// reply "!dlroW olleH"
return string(what.rbegin(), what.rend());
}
};
}
void hello_world(event_based_actor* self, const actor& buddy) {
// send "Hello World!" to our buddy ...
self->sync_send(buddy, "Hello World!").then(
// ... wait for a response ...
[=](const string& what) {
// ... and print it
aout(self) << what << endl;
}
);
// send "Hello World!" to our buddy ...
self->sync_send(buddy, "Hello World!").then(
// ... wait for a response ...
[=](const string& what) {
// ... and print it
aout(self) << what << endl;
}
);
}
int main() {
// create a new actor that calls 'mirror()'
auto mirror_actor = spawn(mirror);
// create another actor that calls 'hello_world(mirror_actor)';
spawn(hello_world, mirror_actor);
// wait until all other actors we have spawned are done
await_all_actors_done();
// run cleanup code before exiting main
shutdown();
// create a new actor that calls 'mirror()'
auto mirror_actor = spawn(mirror);
// create another actor that calls 'hello_world(mirror_actor)';
spawn(hello_world, mirror_actor);
// wait until all other actors we have spawned are done
await_all_actors_done();
// run cleanup code before exiting main
shutdown();
}
/******************************************************************************\
* This example is a very basic, non-interactive math service implemented *
* for both the blocking and the event-based API. *
\******************************************************************************/
\ ******************************************************************************/
#include <tuple>
#include <cassert>
......@@ -15,68 +15,68 @@ using namespace caf;
// implementation using the blocking API
void blocking_math_fun(blocking_actor* self) {
bool done = false;
self->do_receive (
// "arg_match" matches the parameter types of given lambda expression
// thus, it's equal to
// - on<atom("plus"), int, int>()
// - on(atom("plus"), val<int>, val<int>)
on(atom("plus"), arg_match) >> [](int a, int b) {
return std::make_tuple(atom("result"), a + b);
},
on(atom("minus"), arg_match) >> [](int a, int b) {
return std::make_tuple(atom("result"), a - b);
},
on(atom("quit")) >> [&]() {
// note: this actor uses the blocking API, hence self->quit()
// would force stack unwinding by throwing an exception
done = true;
}
).until(done);
bool done = false;
self->do_receive (
// "arg_match" matches the parameter types of given lambda expression
// thus, it's equal to
// - on<atom("plus"), int, int>()
// - on(atom("plus"), val<int>, val<int>)
on(atom("plus"), arg_match) >> [](int a, int b) {
return std::make_tuple(atom("result"), a + b);
},
on(atom("minus"), arg_match) >> [](int a, int b) {
return std::make_tuple(atom("result"), a - b);
},
on(atom("quit")) >> [&]() {
// note: this actor uses the blocking API, hence self->quit()
// would force stack unwinding by throwing an exception
done = true;
}
).until(done);
}
void calculator(event_based_actor* self) {
// execute this behavior until actor terminates
self->become (
on(atom("plus"), arg_match) >> [](int a, int b) {
return std::make_tuple(atom("result"), a + b);
},
on(atom("minus"), arg_match) >> [](int a, int b) {
return std::make_tuple(atom("result"), a - b);
},
on(atom("quit")) >> [=] {
// terminate actor with normal exit reason
self->quit();
}
);
// execute this behavior until actor terminates
self->become (
on(atom("plus"), arg_match) >> [](int a, int b) {
return std::make_tuple(atom("result"), a + b);
},
on(atom("minus"), arg_match) >> [](int a, int b) {
return std::make_tuple(atom("result"), a - b);
},
on(atom("quit")) >> [=] {
// terminate actor with normal exit reason
self->quit();
}
);
}
void tester(event_based_actor* self, const actor& testee) {
self->link_to(testee);
// will be invoked if we receive an unexpected response message
self->on_sync_failure([=] {
aout(self) << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_shutdown);
});
// first test: 2 + 1 = 3
self->sync_send(testee, atom("plus"), 2, 1).then(
on(atom("result"), 3) >> [=] {
// second test: 2 - 1 = 1
self->sync_send(testee, atom("minus"), 2, 1).then(
on(atom("result"), 1) >> [=] {
// both tests succeeded
aout(self) << "AUT (actor under test) seems to be ok"
<< endl;
self->send(testee, atom("quit"));
}
);
self->link_to(testee);
// will be invoked if we receive an unexpected response message
self->on_sync_failure([=] {
aout(self) << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_shutdown);
});
// first test: 2 + 1 = 3
self->sync_send(testee, atom("plus"), 2, 1).then(
on(atom("result"), 3) >> [=] {
// second test: 2 - 1 = 1
self->sync_send(testee, atom("minus"), 2, 1).then(
on(atom("result"), 1) >> [=] {
// both tests succeeded
aout(self) << "AUT (actor under test) seems to be ok"
<< endl;
self->send(testee, atom("quit"));
}
);
);
}
);
}
int main() {
spawn(tester, spawn(calculator));
await_all_actors_done();
shutdown();
return 0;
spawn(tester, spawn(calculator));
await_all_actors_done();
shutdown();
return 0;
}
/******************************************************************************\
* This example illustrates how to do time-triggered loops in libcaf. *
\******************************************************************************/
* This example illustrates how to do time-triggered loops in libcaf. *
\ ******************************************************************************/
#include <chrono>
#include <iostream>
......@@ -15,65 +15,66 @@ using namespace caf;
// ASCII art figures
constexpr const char* figures[] = {
"<(^.^<)",
"<(^.^)>",
"(>^.^)>"
"<(^.^<)",
"<(^.^)>",
"(>^.^)>"
};
struct animation_step { size_t figure_idx; size_t offset; };
// array of {figure, offset} pairs
constexpr animation_step animation_steps[] = {
{1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6},
{2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9},
{1, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {1, 13}, {0, 13},
{0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7}
{1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6},
{2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9},
{1, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {1, 13}, {0, 13},
{0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7}
};
template<typename T, size_t S>
template <class T, size_t S>
constexpr size_t array_size(const T (&) [S]) {
return S;
return S;
}
constexpr size_t animation_width = 20;
// "draws" an animation step by printing "{offset_whitespaces}{figure}{padding}"
void draw_kirby(const animation_step& animation) {
cout.width(animation_width);
// override last figure
cout << '\r';
// print offset
std::fill_n(std::ostream_iterator<char>{cout}, animation.offset, ' ');
// print figure
cout << figures[animation.figure_idx];
// print padding
cout.fill(' ');
// make sure figure is printed
cout.flush();
cout.width(animation_width);
// override last figure
cout << '\r';
// print offset
std::fill_n(std::ostream_iterator<char>{cout}, animation.offset, ' ');
// print figure
cout << figures[animation.figure_idx];
// print padding
cout.fill(' ');
// make sure figure is printed
cout.flush();
}
// uses a message-based loop to iterate over all animation steps
void dancing_kirby(event_based_actor* self) {
// let's get it started
self->send(self, atom("Step"), size_t{0});
self->become (
on(atom("Step"), array_size(animation_steps)) >> [=] {
// we've printed all animation steps (done)
cout << endl;
self->quit();
},
on(atom("Step"), arg_match) >> [=](size_t step) {
// print given step
draw_kirby(animation_steps[step]);
// animate next step in 150ms
self->delayed_send(self, std::chrono::milliseconds(150), atom("Step"), step + 1);
}
);
// let's get it started
self->send(self, atom("Step"), size_t{0});
self->become (
on(atom("Step"), array_size(animation_steps)) >> [=] {
// we've printed all animation steps (done)
cout << endl;
self->quit();
},
on(atom("Step"), arg_match) >> [=](size_t step) {
// print given step
draw_kirby(animation_steps[step]);
// animate next step in 150ms
self->delayed_send(self, std::chrono::milliseconds(150),
atom("Step"), step + 1);
}
);
}
int main() {
spawn(dancing_kirby);
await_all_actors_done();
shutdown();
return 0;
spawn(dancing_kirby);
await_all_actors_done();
shutdown();
return 0;
}
/******************************************************************************\
* This example is an implementation of the classical Dining Philosophers *
* exercise using only libcaf's event-based actor implementation. *
\******************************************************************************/
* exercise using only libcaf's event-based actor implementation. *
\ ******************************************************************************/
#include <map>
#include <vector>
......@@ -20,24 +20,24 @@ namespace {
// either taken by a philosopher or available
void chopstick(event_based_actor* self) {
self->become(
on(atom("take"), arg_match) >> [=](const actor& philos) {
// tell philosopher it took this chopstick
self->send(philos, atom("taken"), self);
// await 'put' message and reject other 'take' messages
self->become(
// allows us to return to the previous behavior
keep_behavior,
on(atom("take"), arg_match) >> [=](const actor& other) {
self->send(other, atom("busy"), self);
},
on(atom("put"), philos) >> [=] {
// return to previous behaivor, i.e., await next 'take'
self->unbecome();
}
);
self->become(
on(atom("take"), arg_match) >> [=](const actor& philos) {
// tell philosopher it took this chopstick
self->send(philos, atom("taken"), self);
// await 'put' message and reject other 'take' messages
self->become(
// allows us to return to the previous behavior
keep_behavior,
on(atom("take"), arg_match) >> [=](const actor& other) {
self->send(other, atom("busy"), self);
},
on(atom("put"), philos) >> [=] {
// return to previous behaivor, i.e., await next 'take'
self->unbecome();
}
);
);
}
);
}
/* See: http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
......@@ -73,127 +73,122 @@ void chopstick(event_based_actor* self) {
* [ X = right => Y = left ]
*/
struct philosopher : event_based_actor {
std::string name; // the name of this philosopher
actor left; // left chopstick
actor right; // right chopstick
// note: we have to define all behaviors in the constructor because
// non-static member initialization are not (yet) implemented in GCC
behavior thinking;
behavior hungry;
behavior denied;
behavior eating;
// wait for second chopstick
behavior waiting_for(const actor& what) {
return (
on(atom("taken"), what) >> [=] {
aout(this) << name
<< " has picked up chopsticks with IDs "
<< left->id()
<< " and "
<< right->id()
<< " and starts to eat\n";
// eat some time
delayed_send(this, seconds(5), atom("think"));
become(eating);
},
on(atom("busy"), what) >> [=] {
send((what == left) ? right : left, atom("put"), this);
send(this, atom("eat"));
become(thinking);
}
);
}
philosopher(const std::string& n, const actor& l, const actor& r)
: name(n), left(l), right(r) {
// a philosopher that receives {eat} stops thinking and becomes hungry
thinking = (
on(atom("eat")) >> [=] {
become(hungry);
send(left, atom("take"), this);
send(right, atom("take"), this);
}
);
// wait for the first answer of a chopstick
hungry = (
on(atom("taken"), left) >> [=] {
become(waiting_for(right));
},
on(atom("taken"), right) >> [=] {
become(waiting_for(left));
},
on<atom("busy"), actor>() >> [=] {
become(denied);
}
);
// philosopher was not able to obtain the first chopstick
denied = (
on(atom("taken"), arg_match) >> [=](const actor& ptr) {
send(ptr, atom("put"), this);
send(this, atom("eat"));
become(thinking);
},
on<atom("busy"), actor>() >> [=] {
send(this, atom("eat"));
become(thinking);
}
);
// philosopher obtained both chopstick and eats (for five seconds)
eating = (
on(atom("think")) >> [=] {
send(left, atom("put"), this);
send(right, atom("put"), this);
delayed_send(this, seconds(5), atom("eat"));
aout(this) << name
<< " puts down his chopsticks and starts to think\n";
become(thinking);
}
);
}
behavior make_behavior() override {
// start thinking
send(this, atom("think"));
// philosophers start to think after receiving {think}
return (
on(atom("think")) >> [=] {
aout(this) << name << " starts to think\n";
delayed_send(this, seconds(5), atom("eat"));
become(thinking);
}
);
}
class philosopher : public event_based_actor {
public:
philosopher(const std::string& n, const actor& l, const actor& r)
: name(n), left(l), right(r) {
// a philosopher that receives {eat} stops thinking and becomes hungry
thinking = (
on(atom("eat")) >> [=] {
become(hungry);
send(left, atom("take"), this);
send(right, atom("take"), this);
}
);
// wait for the first answer of a chopstick
hungry = (
on(atom("taken"), left) >> [=] {
become(waiting_for(right));
},
on(atom("taken"), right) >> [=] {
become(waiting_for(left));
},
on<atom("busy"), actor>() >> [=] {
become(denied);
}
);
// philosopher was not able to obtain the first chopstick
denied = (
on(atom("taken"), arg_match) >> [=](const actor& ptr) {
send(ptr, atom("put"), this);
send(this, atom("eat"));
become(thinking);
},
on<atom("busy"), actor>() >> [=] {
send(this, atom("eat"));
become(thinking);
}
);
// philosopher obtained both chopstick and eats (for five seconds)
eating = (
on(atom("think")) >> [=] {
send(left, atom("put"), this);
send(right, atom("put"), this);
delayed_send(this, seconds(5), atom("eat"));
aout(this) << name << " puts down his chopsticks and starts to think\n";
become(thinking);
}
);
}
protected:
behavior make_behavior() override {
// start thinking
send(this, atom("think"));
// philosophers start to think after receiving {think}
return (
on(atom("think")) >> [=] {
aout(this) << name << " starts to think\n";
delayed_send(this, seconds(5), atom("eat"));
become(thinking);
}
);
}
private:
// wait for second chopstick
behavior waiting_for(const actor& what) {
return {
on(atom("taken"), what) >> [=] {
aout(this) << name
<< " has picked up chopsticks with IDs "
<< left->id() << " and " << right->id()
<< " and starts to eat\n";
// eat some time
delayed_send(this, seconds(5), atom("think"));
become(eating);
},
on(atom("busy"), what) >> [=] {
send((what == left) ? right : left, atom("put"), this);
send(this, atom("eat"));
become(thinking);
}
};
}
std::string name; // the name of this philosopher
actor left; // left chopstick
actor right; // right chopstick
behavior thinking;
behavior hungry; // tries to take chopsticks
behavior denied; // could not get chopsticks
behavior eating; // wait for some time, then go thinking again
};
void dining_philosophers() {
scoped_actor self;
// create five chopsticks
aout(self) << "chopstick ids are:";
std::vector<actor> chopsticks;
for (size_t i = 0; i < 5; ++i) {
chopsticks.push_back(spawn(chopstick));
aout(self) << " " << chopsticks.back()->id();
}
aout(self) << endl;
// spawn five philosophers
std::vector<std::string> names { "Plato", "Hume", "Kant",
"Nietzsche", "Descartes" };
for (size_t i = 0; i < 5; ++i) {
spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i+1)%5]);
}
scoped_actor self;
// create five chopsticks
aout(self) << "chopstick ids are:";
std::vector<actor> chopsticks;
for (size_t i = 0; i < 5; ++i) {
chopsticks.push_back(spawn(chopstick));
aout(self) << " " << chopsticks.back()->id();
}
aout(self) << endl;
// spawn five philosophers
std::vector<std::string> names {"Plato", "Hume", "Kant",
"Nietzsche", "Descartes"};
for (size_t i = 0; i < 5; ++i) {
spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]);
}
}
} // namespace <anonymous>
int main(int, char**) {
dining_philosophers();
// real philosophers are never done
await_all_actors_done();
shutdown();
return 0;
dining_philosophers();
// real philosophers are never done
await_all_actors_done();
shutdown();
return 0;
}
/******************************************************************************\
* This example is a very basic, non-interactive math service implemented *
* using typed actors. *
\******************************************************************************/
\ ******************************************************************************/
#include <cassert>
#include <iostream>
......@@ -16,82 +16,80 @@ struct shutdown_request { };
struct plus_request { int a; int b; };
struct minus_request { int a; int b; };
using calculator_type = typed_actor<
replies_to<plus_request>::with<int>,
replies_to<minus_request>::with<int>,
replies_to<shutdown_request>::with<void>
>;
using calculator_type = typed_actor<replies_to<plus_request>::with<int>,
replies_to<minus_request>::with<int>,
replies_to<shutdown_request>::with<void>>;
calculator_type::behavior_type typed_calculator(calculator_type::pointer self) {
return {
[](const plus_request& pr) {
return pr.a + pr.b;
},
[](const minus_request& pr) {
return pr.a - pr.b;
},
[=](const shutdown_request&) {
self->quit();
}
};
return {
[](const plus_request& pr) {
return pr.a + pr.b;
},
[](const minus_request& pr) {
return pr.a - pr.b;
},
[=](const shutdown_request&) {
self->quit();
}
};
}
class typed_calculator_class : public calculator_type::base {
protected:
behavior_type make_behavior() override {
return {
[](const plus_request& pr) {
return pr.a + pr.b;
},
[](const minus_request& pr) {
return pr.a - pr.b;
},
[=](const shutdown_request&) {
quit();
}
};
}
behavior_type make_behavior() override {
return {
[](const plus_request& pr) {
return pr.a + pr.b;
},
[](const minus_request& pr) {
return pr.a - pr.b;
},
[=](const shutdown_request&) {
quit();
}
};
}
};
void tester(event_based_actor* self, const calculator_type& testee) {
self->link_to(testee);
// will be invoked if we receive an unexpected response message
self->on_sync_failure([=] {
aout(self) << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_shutdown);
});
// first test: 2 + 1 = 3
self->sync_send(testee, plus_request{2, 1}).then(
[=](int r1) {
assert(r1 == 3);
// second test: 2 - 1 = 1
self->sync_send(testee, minus_request{2, 1}).then(
[=](int r2) {
assert(r2 == 1);
// both tests succeeded
aout(self) << "AUT (actor under test) seems to be ok"
<< endl;
self->send(testee, shutdown_request{});
}
);
self->link_to(testee);
// will be invoked if we receive an unexpected response message
self->on_sync_failure([=] {
aout(self) << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_shutdown);
});
// first test: 2 + 1 = 3
self->sync_send(testee, plus_request{2, 1}).then(
[=](int r1) {
assert(r1 == 3);
// second test: 2 - 1 = 1
self->sync_send(testee, minus_request{2, 1}).then(
[=](int r2) {
assert(r2 == 1);
// both tests succeeded
aout(self) << "AUT (actor under test) seems to be ok"
<< endl;
self->send(testee, shutdown_request{});
}
);
);
}
);
}
} // namespace <anonymous>
int main() {
// announce custom message types
announce<shutdown_request>();
announce<plus_request>(&plus_request::a, &plus_request::b);
announce<minus_request>(&minus_request::a, &minus_request::b);
// test function-based impl
spawn(tester, spawn_typed(typed_calculator));
await_all_actors_done();
// test class-based impl
spawn(tester, spawn_typed<typed_calculator_class>());
await_all_actors_done();
// done
shutdown();
return 0;
// announce custom message types
announce<shutdown_request>();
announce<plus_request>(&plus_request::a, &plus_request::b);
announce<minus_request>(&minus_request::a, &minus_request::b);
// test function-based impl
spawn(tester, spawn_typed(typed_calculator));
await_all_actors_done();
// test class-based impl
spawn(tester, spawn_typed<typed_calculator_class>());
await_all_actors_done();
// done
shutdown();
return 0;
}
This diff is collapsed.
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2014 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* Raphael Hiesgen <raphael.hiesgen@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2014 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* Raphael Hiesgen <raphael.hiesgen@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation, either version 3 of the License *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation, either version 3 of the License *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\ ******************************************************************************/
#include <vector>
#include <iomanip>
......@@ -47,78 +47,78 @@ constexpr const char* kernel_name = "matrix_mult";
// opencl kernel, multiplies matrix1 and matrix2
// last parameter is, by convention, the output parameter
constexpr const char* kernel_source = R"__(
__kernel void matrix_mult(__global float* matrix1,
__global float* matrix2,
__global float* output) {
// we only use square matrices, hence: width == height
size_t size = get_global_size(0); // == get_global_size_(1);
size_t x = get_global_id(0);
size_t y = get_global_id(1);
float result = 0;
for (size_t idx = 0; idx < size; ++idx) {
result += matrix1[idx + y * size] * matrix2[x + idx * size];
}
output[x+y*size] = result;
__kernel void matrix_mult(__global float* matrix1,
__global float* matrix2,
__global float* output) {
// we only use square matrices, hence: width == height
size_t size = get_global_size(0); // == get_global_size_(1);
size_t x = get_global_id(0);
size_t y = get_global_id(1);
float result = 0;
for (size_t idx = 0; idx < size; ++idx) {
result += matrix1[idx + y * size] * matrix2[x + idx * size];
}
output[x+y*size] = result;
}
)__";
} // namespace <anonymous>
void print_as_matrix(const vector<float>& matrix) {
for (size_t column = 0; column < matrix_size; ++column) {
for (size_t row = 0; row < matrix_size; ++row) {
cout << fixed << setprecision(2) << setw(9)
<< matrix[row + column * matrix_size];
}
cout << endl;
for (size_t column = 0; column < matrix_size; ++column) {
for (size_t row = 0; row < matrix_size; ++row) {
cout << fixed << setprecision(2) << setw(9)
<< matrix[row + column * matrix_size];
}
cout << endl;
}
}
void multiplier(event_based_actor* self) {
// the opencl actor only understands vectors
// so these vectors represent the matrices
vector<float> m1(matrix_size * matrix_size);
vector<float> m2(matrix_size * matrix_size);
// the opencl actor only understands vectors
// so these vectors represent the matrices
vector<float> m1(matrix_size * matrix_size);
vector<float> m2(matrix_size * matrix_size);
// fill each with ascending values
iota(m1.begin(), m1.end(), 0);
iota(m2.begin(), m2.end(), 0);
// fill each with ascending values
iota(m1.begin(), m1.end(), 0);
iota(m2.begin(), m2.end(), 0);
// print "source" matrix
cout << "calculating square of matrix:" << endl;
print_as_matrix(m1);
cout << endl;
// print "source" matrix
cout << "calculating square of matrix:" << endl;
print_as_matrix(m1);
cout << endl;
// spawn an opencl actor
// template parameter: signature of opencl kernel using proper return type
// instead of output parameter (implicitly
// mapped to the last kernel argument)
// 1st arg: source code of one or more kernels
// 2nd arg: name of the kernel to use
// 3rd arg: global dimension arguments for opencl's enqueue;
// creates matrix_size * matrix_size global work items
// 4th arg: offsets for global dimensions (optional)
// 5th arg: local dimensions (optional)
// 6th arg: number of elements in the result buffer
auto worker = spawn_cl<float*(float*,float*)>(kernel_source,
kernel_name,
{matrix_size, matrix_size},
{},
{},
matrix_size * matrix_size);
// send both matrices to the actor and wait for a result
self->sync_send(worker, move(m1), move(m2)).then(
[](const vector<float>& result) {
cout << "result: " << endl;
print_as_matrix(result);
}
);
// spawn an opencl actor
// template parameter: signature of opencl kernel using proper return type
// instead of output parameter (implicitly
// mapped to the last kernel argument)
// 1st arg: source code of one or more kernels
// 2nd arg: name of the kernel to use
// 3rd arg: global dimension arguments for opencl's enqueue;
// creates matrix_size * matrix_size global work items
// 4th arg: offsets for global dimensions (optional)
// 5th arg: local dimensions (optional)
// 6th arg: number of elements in the result buffer
auto worker = spawn_cl<float*(float*,float*)>(kernel_source,
kernel_name,
{matrix_size, matrix_size},
{},
{},
matrix_size * matrix_size);
// send both matrices to the actor and wait for a result
self->sync_send(worker, move(m1), move(m2)).then(
[](const vector<float>& result) {
cout << "result: " << endl;
print_as_matrix(result);
}
);
}
int main() {
announce<vector<float>>();
spawn(multiplier);
await_all_actors_done();
shutdown();
return 0;
announce<vector<float>>();
spawn(multiplier);
await_all_actors_done();
shutdown();
return 0;
}
......@@ -6,7 +6,7 @@
* - ./build/bin/group_server -p 4242 *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\******************************************************************************/
\ ******************************************************************************/
#include <set>
#include <map>
......@@ -26,136 +26,132 @@ using namespace caf;
struct line { string str; };
istream& operator>>(istream& is, line& l) {
getline(is, l.str);
return is;
getline(is, l.str);
return is;
}
namespace { string s_last_line; }
message split_line(const line& l) {
istringstream strs(l.str);
s_last_line = move(l.str);
string tmp;
message_builder mb;
while (getline(strs, tmp, ' ')) {
if (!tmp.empty()) mb.append(std::move(tmp));
}
return mb.to_message();
istringstream strs(l.str);
s_last_line = move(l.str);
string tmp;
message_builder mb;
while (getline(strs, tmp, ' ')) {
if (!tmp.empty()) mb.append(std::move(tmp));
}
return mb.to_message();
}
void client(event_based_actor* self, const string& name) {
self->become (
on(atom("broadcast"), arg_match) >> [=](const string& message) {
for(auto& dest : self->joined_groups()) {
self->send(dest, name + ": " + message);
}
},
on(atom("join"), arg_match) >> [=](const group& what) {
for (auto g : self->joined_groups()) {
cout << "*** leave " << to_string(g) << endl;
self->send(self, g, name + " has left the chatroom");
self->leave(g);
}
cout << "*** join " << to_string(what) << endl;
self->join(what);
self->send(what, name + " has entered the chatroom");
},
[=](const string& txt) {
// don't print own messages
if (self->last_sender() != self) cout << txt << endl;
},
[=](const group_down_msg& g) {
cout << "*** chatroom offline: " << to_string(g.source) << endl;
},
others() >> [=]() {
cout << "unexpected: " << to_string(self->last_dequeued()) << endl;
}
);
self->become (
on(atom("broadcast"), arg_match) >> [=](const string& message) {
for(auto& dest : self->joined_groups()) {
self->send(dest, name + ": " + message);
}
},
on(atom("join"), arg_match) >> [=](const group& what) {
for (auto g : self->joined_groups()) {
cout << "*** leave " << to_string(g) << endl;
self->send(self, g, name + " has left the chatroom");
self->leave(g);
}
cout << "*** join " << to_string(what) << endl;
self->join(what);
self->send(what, name + " has entered the chatroom");
},
[=](const string& txt) {
// don't print own messages
if (self->last_sender() != self) cout << txt << endl;
},
[=](const group_down_msg& g) {
cout << "*** chatroom offline: " << to_string(g.source) << endl;
},
others() >> [=]() {
cout << "unexpected: " << to_string(self->last_dequeued()) << endl;
}
);
}
int main(int argc, char** argv) {
string name;
string group_id;
options_description desc;
bool args_valid = match_stream<string>(argv + 1, argv + argc) (
on_opt1('n', "name", &desc, "set name") >> rd_arg(name),
on_opt1('g', "group", &desc, "join group <arg1>") >> rd_arg(group_id),
on_opt0('h', "help", &desc, "print help") >> print_desc_and_exit(&desc)
);
if (!args_valid) print_desc_and_exit(&desc)();
while (name.empty()) {
cout << "please enter your name: " << flush;
if (!getline(cin, name)) {
cerr << "*** no name given... terminating" << endl;
return 1;
}
string name;
string group_id;
options_description desc;
bool args_valid = match_stream<string>(argv + 1, argv + argc) (
on_opt1('n', "name", &desc, "set name") >> rd_arg(name),
on_opt1('g', "group", &desc, "join group <arg1>") >> rd_arg(group_id),
on_opt0('h', "help", &desc, "print help") >> print_desc_and_exit(&desc)
);
if (!args_valid) {
print_desc_and_exit(&desc)();
}
while (name.empty()) {
cout << "please enter your name: " << flush;
if (!getline(cin, name)) {
cerr << "*** no name given... terminating" << endl;
return 1;
}
auto client_actor = spawn(client, name);
// evaluate group parameters
if (!group_id.empty()) {
auto p = group_id.find(':');
if (p == std::string::npos) {
cerr << "*** error parsing argument " << group_id
<< ", expected format: <module_name>:<group_id>";
}
else {
try {
auto g = group::get(group_id.substr(0, p),
group_id.substr(p + 1));
anon_send(client_actor, atom("join"), g);
}
catch (exception& e) {
ostringstream err;
cerr << "*** exception: group::get(\"" << group_id.substr(0, p)
<< "\", \"" << group_id.substr(p + 1) << "\") failed; "
<< to_verbose_string(e) << endl;
}
}
}
auto client_actor = spawn(client, name);
// evaluate group parameters
if (!group_id.empty()) {
auto p = group_id.find(':');
if (p == std::string::npos) {
cerr << "*** error parsing argument " << group_id
<< ", expected format: <module_name>:<group_id>";
} else {
try {
auto g = group::get(group_id.substr(0, p),
group_id.substr(p + 1));
anon_send(client_actor, atom("join"), g);
}
catch (exception& e) {
ostringstream err;
cerr << "*** exception: group::get(\"" << group_id.substr(0, p)
<< "\", \"" << group_id.substr(p + 1) << "\") failed; "
<< to_verbose_string(e) << endl;
}
}
cout << "*** starting client, type '/help' for a list of commands" << endl;
auto starts_with = [](const string& str) -> function<optional<string> (const string&)> {
return [=](const string& arg) -> optional<string> {
if (arg.compare(0, str.size(), str) == 0) {
return arg;
}
return none;
};
}
cout << "*** starting client, type '/help' for a list of commands" << endl;
auto starts_with = [](const string& str) -> function<optional<string> (const string&)> {
return [=](const string& arg) -> optional<string> {
if (arg.compare(0, str.size(), str) == 0) {
return arg;
}
return none;
};
istream_iterator<line> lines(cin);
istream_iterator<line> eof;
match_each (lines, eof, split_line) (
on("/join", arg_match) >> [&](const string& mod, const string& id) {
try {
anon_send(client_actor, atom("join"), group::get(mod, id));
}
catch (exception& e) {
cerr << "*** exception: " << to_verbose_string(e) << endl;
}
},
on("/quit") >> [&] {
// close STDIN; causes this match loop to quit
cin.setstate(ios_base::eofbit);
},
on(starts_with("/"), any_vals) >> [&] {
cout << "*** available commands:\n"
" /join <module> <group> join a new chat channel\n"
" /quit quit the program\n"
" /help print this text\n" << flush;
},
others() >> [&] {
if (!s_last_line.empty()) {
anon_send(client_actor, atom("broadcast"), s_last_line);
}
}
);
// force actor to quit
anon_send_exit(client_actor, exit_reason::user_shutdown);
await_all_actors_done();
shutdown();
return 0;
};
istream_iterator<line> lines(cin);
istream_iterator<line> eof;
match_each (lines, eof, split_line) (
on("/join", arg_match) >> [&](const string& mod, const string& id) {
try {
anon_send(client_actor, atom("join"), group::get(mod, id));
}
catch (exception& e) {
cerr << "*** exception: " << to_verbose_string(e) << endl;
}
},
on("/quit") >> [&] {
// close STDIN; causes this match loop to quit
cin.setstate(ios_base::eofbit);
},
on(starts_with("/"), any_vals) >> [&] {
cout << "*** available commands:\n"
" /join <module> <group> join a new chat channel\n"
" /quit quit the program\n"
" /help print this text\n" << flush;
},
others() >> [&] {
if (!s_last_line.empty()) {
anon_send(client_actor, atom("broadcast"), s_last_line);
}
}
);
// force actor to quit
anon_send_exit(client_actor, exit_reason::user_shutdown);
await_all_actors_done();
shutdown();
return 0;
}
......@@ -6,7 +6,7 @@
* - ./build/bin/group_server -p 4242 *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\******************************************************************************/
\ ******************************************************************************/
#include <string>
#include <iostream>
......@@ -20,46 +20,46 @@ using namespace std;
using namespace caf;
int main(int argc, char** argv) {
uint16_t port = 0;
options_description desc;
bool args_valid = match_stream<string>(argv + 1, argv + argc) (
on_opt1('p', "port", &desc, "set port") >> rd_arg(port),
on_opt0('h', "help", &desc, "print help") >> print_desc_and_exit(&desc)
);
if (port <= 1024) {
cerr << "*** no port > 1024 given" << endl;
args_valid = false;
}
if (!args_valid) {
// print_desc(&desc) returns a function printing the stored help text
auto desc_printer = print_desc(&desc);
desc_printer();
return 1;
}
try {
// try to bind the group server to the given port,
// this allows other nodes to access groups of this server via
// group::get("remote", "<group>@<host>:<port>");
// note: it is not needed to explicitly create a <group> on the server,
// as groups are created on-the-fly on first usage
io::publish_local_groups(port);
}
catch (bind_failure& e) {
// thrown if <port> is already in use
cerr << "*** bind_failure: " << e.what() << endl;
return 2;
}
catch (network_error& e) {
// thrown on errors in the socket API
cerr << "*** network error: " << e.what() << endl;
return 2;
}
cout << "type 'quit' to shutdown the server" << endl;
string line;
while (getline(cin, line)) {
if (line == "quit") return 0;
else cout << "illegal command" << endl;
}
shutdown();
return 0;
uint16_t port = 0;
options_description desc;
bool args_valid = match_stream<string>(argv + 1, argv + argc) (
on_opt1('p', "port", &desc, "set port") >> rd_arg(port),
on_opt0('h', "help", &desc, "print help") >> print_desc_and_exit(&desc)
);
if (port <= 1024) {
cerr << "*** no port > 1024 given" << endl;
args_valid = false;
}
if (!args_valid) {
// print_desc(&desc) returns a function printing the stored help text
auto desc_printer = print_desc(&desc);
desc_printer();
return 1;
}
try {
// try to bind the group server to the given port,
// this allows other nodes to access groups of this server via
// group::get("remote", "<group>@<host>:<port>");
// note: it is not needed to explicitly create a <group> on the server,
// as groups are created on-the-fly on first usage
io::publish_local_groups(port);
}
catch (bind_failure& e) {
// thrown if <port> is already in use
cerr << "*** bind_failure: " << e.what() << endl;
return 2;
}
catch (network_error& e) {
// thrown on errors in the socket API
cerr << "*** network error: " << e.what() << endl;
return 2;
}
cout << "type 'quit' to shutdown the server" << endl;
string line;
while (getline(cin, line)) {
if (line == "quit") return 0;
else cout << "illegal command" << endl;
}
shutdown();
return 0;
}
......@@ -8,6 +8,6 @@ message Pong {
}
message PingOrPong {
optional Ping ping = 1;
optional Pong pong = 2;
optional Ping ping = 1;
optional Pong pong = 2;
}
This diff is collapsed.
This diff is collapsed.
......@@ -15,14 +15,14 @@ using namespace caf;
// POD struct
struct foo {
std::vector<int> a;
int b;
std::vector<int> a;
int b;
};
// announce requires foo to have the equal operator implemented
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a == rhs.a
&& lhs.b == rhs.b;
return lhs.a == rhs.a
&& lhs.b == rhs.b;
}
// a pair of two ints
......@@ -33,95 +33,95 @@ using foo_pair2 = std::pair<int, int>;
// a struct with member vector<vector<...>>
struct foo2 {
int a;
vector<vector<double>> b;
int a;
vector<vector<double>> b;
};
bool operator==( const foo2& lhs, const foo2& rhs ) {
return lhs.a == rhs.a && lhs.b == rhs.b;
return lhs.a == rhs.a && lhs.b == rhs.b;
}
// receives `remaining` messages
void testee(event_based_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
self->become (
// note: we sent a foo_pair2, but match on foo_pair
// that's safe because both are aliases for std::pair<int, int>
on<foo_pair>() >> [=](const foo_pair& val) {
cout << "foo_pair("
<< val.first << ", "
<< val.second << ")"
<< endl;
set_next_behavior();
},
on<foo>() >> [=](const foo& val) {
cout << "foo({";
auto i = val.a.begin();
auto end = val.a.end();
if (i != end) {
cout << *i;
while (++i != end) {
cout << ", " << *i;
}
}
cout << "}, " << val.b << ")" << endl;
set_next_behavior();
auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
self->become (
// note: we sent a foo_pair2, but match on foo_pair
// that's safe because both are aliases for std::pair<int, int>
on<foo_pair>() >> [=](const foo_pair& val) {
cout << "foo_pair("
<< val.first << ", "
<< val.second << ")"
<< endl;
set_next_behavior();
},
on<foo>() >> [=](const foo& val) {
cout << "foo({";
auto i = val.a.begin();
auto end = val.a.end();
if (i != end) {
cout << *i;
while (++i != end) {
cout << ", " << *i;
}
);
}
cout << "}, " << val.b << ")" << endl;
set_next_behavior();
}
);
}
int main(int, char**) {
// announces foo to the libcaf type system;
// the function expects member pointers to all elements of foo
announce<foo>(&foo::a, &foo::b);
// announce foo2 to the libcaf type system,
// note that recursive containers are managed automatically by libcaf
announce<foo2>(&foo2::a, &foo2::b);
foo2 vd;
vd.a = 5;
vd.b.resize(1);
vd.b.back().push_back(42);
vector<char> buf;
binary_serializer bs(std::back_inserter(buf));
bs << vd;
binary_deserializer bd(buf.data(), buf.size());
foo2 vd2;
uniform_typeid<foo2>()->deserialize(&vd2, &bd);
assert(vd == vd2);
// announce std::pair<int, int> to the type system;
// NOTE: foo_pair is NOT distinguishable from foo_pair2!
auto uti = announce<foo_pair>(&foo_pair::first, &foo_pair::second);
// since foo_pair and foo_pair2 are not distinguishable since typedefs
// do not 'create' an own type, this announce fails, since
// std::pair<int, int> is already announced
assert(announce<foo_pair2>(&foo_pair2::first, &foo_pair2::second) == uti);
// libcaf returns the same uniform_type_info
// instance for foo_pair and foo_pair2
assert(uniform_typeid<foo_pair>() == uniform_typeid<foo_pair2>());
// spawn a testee that receives two messages
auto t = spawn(testee, 2);
{
scoped_actor self;
// send t a foo
self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2
self->send(t, foo_pair2{3, 4});
}
await_all_actors_done();
shutdown();
return 0;
// announces foo to the libcaf type system;
// the function expects member pointers to all elements of foo
announce<foo>(&foo::a, &foo::b);
// announce foo2 to the libcaf type system,
// note that recursive containers are managed automatically by libcaf
announce<foo2>(&foo2::a, &foo2::b);
foo2 vd;
vd.a = 5;
vd.b.resize(1);
vd.b.back().push_back(42);
vector<char> buf;
binary_serializer bs(std::back_inserter(buf));
bs << vd;
binary_deserializer bd(buf.data(), buf.size());
foo2 vd2;
uniform_typeid<foo2>()->deserialize(&vd2, &bd);
assert(vd == vd2);
// announce std::pair<int, int> to the type system;
// NOTE: foo_pair is NOT distinguishable from foo_pair2!
auto uti = announce<foo_pair>(&foo_pair::first, &foo_pair::second);
// since foo_pair and foo_pair2 are not distinguishable since typedefs
// do not 'create' an own type, this announce fails, since
// std::pair<int, int> is already announced
assert(announce<foo_pair2>(&foo_pair2::first, &foo_pair2::second) == uti);
// libcaf returns the same uniform_type_info
// instance for foo_pair and foo_pair2
assert(uniform_typeid<foo_pair>() == uniform_typeid<foo_pair2>());
// spawn a testee that receives two messages
auto t = spawn(testee, 2);
{
scoped_actor self;
// send t a foo
self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2
self->send(t, foo_pair2{3, 4});
}
await_all_actors_done();
shutdown();
return 0;
}
......@@ -12,56 +12,56 @@ using namespace caf;
// a simple class using getter and setter member functions
class foo {
int m_a;
int m_b;
int m_a;
int m_b;
public:
foo(int a0 = 0, int b0 = 0) : m_a(a0), m_b(b0) { }
foo(int a0 = 0, int b0 = 0) : m_a(a0), m_b(b0) { }
foo(const foo&) = default;
foo(const foo&) = default;
foo& operator=(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const { return m_a; }
int a() const { return m_a; }
void set_a(int val) { m_a = val; }
void set_a(int val) { m_a = val; }
int b() const { return m_b; }
int b() const { return m_b; }
void set_b(int val) { m_b = val; }
void set_b(int val) { m_b = val; }
};
// announce requires foo to be comparable
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
}
void testee(event_based_actor* self) {
self->become (
on<foo>() >> [=](const foo& val) {
aout(self) << "foo("
<< val.a() << ", "
<< val.b() << ")"
<< endl;
self->quit();
}
);
self->become (
on<foo>() >> [=](const foo& val) {
aout(self) << "foo("
<< val.a() << ", "
<< val.b() << ")"
<< endl;
self->quit();
}
);
}
int main(int, char**) {
// if a class uses getter and setter member functions,
// we pass those to the announce function as { getter, setter } pairs.
announce<foo>(make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b));
{
scoped_actor self;
auto t = spawn(testee);
self->send(t, foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
// if a class uses getter and setter member functions,
// we pass those to the announce function as { getter, setter } pairs.
announce<foo>(make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b));
{
scoped_actor self;
auto t = spawn(testee);
self->send(t, foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
}
......@@ -12,33 +12,33 @@ using namespace caf;
// a simple class using overloaded getter and setter member functions
class foo {
int m_a;
int m_b;
int m_a;
int m_b;
public:
foo() : m_a(0), m_b(0) { }
foo() : m_a(0), m_b(0) { }
foo(int a0, int b0) : m_a(a0), m_b(b0) { }
foo(int a0, int b0) : m_a(a0), m_b(b0) { }
foo(const foo&) = default;
foo(const foo&) = default;
foo& operator=(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const { return m_a; }
int a() const { return m_a; }
void a(int val) { m_a = val; }
void a(int val) { m_a = val; }
int b() const { return m_b; }
int b() const { return m_b; }
void b(int val) { m_b = val; }
void b(int val) { m_b = val; }
};
// announce requires foo to have the equal operator implemented
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
}
// a member function pointer to get an attribute of foo
......@@ -47,43 +47,43 @@ using foo_getter = int (foo::*)() const;
using foo_setter = void (foo::*)(int);
void testee(event_based_actor* self) {
self->become (
on<foo>() >> [=](const foo& val) {
aout(self) << "foo("
<< val.a() << ", "
<< val.b() << ")"
<< endl;
self->quit();
}
);
self->become (
on<foo>() >> [=](const foo& val) {
aout(self) << "foo("
<< val.a() << ", "
<< val.b() << ")"
<< endl;
self->quit();
}
);
}
int main(int, char**) {
// since the member function "a" is ambiguous, the compiler
// also needs a type to select the correct overload
foo_getter g1 = &foo::a;
foo_setter s1 = &foo::a;
// same is true for b
foo_getter g2 = &foo::b;
foo_setter s2 = &foo::b;
// equal to example 3
announce<foo>(make_pair(g1, s1), make_pair(g2, s2));
// alternative syntax that uses casts instead of variables
// (returns false since foo is already announced)
announce<foo>(make_pair(static_cast<foo_getter>(&foo::a),
static_cast<foo_setter>(&foo::a)),
make_pair(static_cast<foo_getter>(&foo::b),
static_cast<foo_setter>(&foo::b)));
// spawn a new testee and send it a foo
{
scoped_actor self;
self->send(spawn(testee), foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
// since the member function "a" is ambiguous, the compiler
// also needs a type to select the correct overload
foo_getter g1 = &foo::a;
foo_setter s1 = &foo::a;
// same is true for b
foo_getter g2 = &foo::b;
foo_setter s2 = &foo::b;
// equal to example 3
announce<foo>(make_pair(g1, s1), make_pair(g2, s2));
// alternative syntax that uses casts instead of variables
// (returns false since foo is already announced)
announce<foo>(make_pair(static_cast<foo_getter>(&foo::a),
static_cast<foo_setter>(&foo::a)),
make_pair(static_cast<foo_getter>(&foo::b),
static_cast<foo_setter>(&foo::b)));
// spawn a new testee and send it a foo
{
scoped_actor self;
self->send(spawn(testee), foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
}
......@@ -11,129 +11,129 @@ using namespace caf;
// the foo class from example 3
class foo {
int m_a;
int m_b;
int m_a;
int m_b;
public:
foo() : m_a(0), m_b(0) { }
foo() : m_a(0), m_b(0) { }
foo(int a0, int b0) : m_a(a0), m_b(b0) { }
foo(int a0, int b0) : m_a(a0), m_b(b0) { }
foo(const foo&) = default;
foo(const foo&) = default;
foo& operator=(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const { return m_a; }
int a() const { return m_a; }
void set_a(int val) { m_a = val; }
void set_a(int val) { m_a = val; }
int b() const { return m_b; }
int b() const { return m_b; }
void set_b(int val) { m_b = val; }
void set_b(int val) { m_b = val; }
};
// needed for operator==() of bar
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
}
// simple struct that has foo as a member
struct bar {
foo f;
int i;
foo f;
int i;
};
// announce requires bar to have the equal operator implemented
bool operator==(const bar& lhs, const bar& rhs) {
return lhs.f == rhs.f
&& lhs.i == rhs.i;
return lhs.f == rhs.f
&& lhs.i == rhs.i;
}
// "worst case" class ... not a good software design at all ;)
class baz {
foo m_f;
foo m_f;
public:
bar b;
bar b;
// announce requires a default constructor
baz() = default;
// announce requires a default constructor
baz() = default;
inline baz(const foo& mf, const bar& mb) : m_f(mf), b(mb) { }
inline baz(const foo& mf, const bar& mb) : m_f(mf), b(mb) { }
const foo& f() const { return m_f; }
const foo& f() const { return m_f; }
void set_f(const foo& val) { m_f = val; }
void set_f(const foo& val) { m_f = val; }
};
// even worst case classes have to implement operator==
bool operator==(const baz& lhs, const baz& rhs) {
return lhs.f() == rhs.f()
&& lhs.b == rhs.b;
return lhs.f() == rhs.f()
&& lhs.b == rhs.b;
}
// receives `remaining` messages
void testee(event_based_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
self->become (
on<bar>() >> [=](const bar& val) {
aout(self) << "bar(foo("
<< val.f.a() << ", "
<< val.f.b() << "), "
<< val.i << ")"
<< endl;
set_next_behavior();
},
on<baz>() >> [=](const baz& val) {
// prints: baz ( foo ( 1, 2 ), bar ( foo ( 3, 4 ), 5 ) )
aout(self) << to_string(make_message(val)) << endl;
set_next_behavior();
}
);
auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
self->become (
on<bar>() >> [=](const bar& val) {
aout(self) << "bar(foo("
<< val.f.a() << ", "
<< val.f.b() << "), "
<< val.i << ")"
<< endl;
set_next_behavior();
},
on<baz>() >> [=](const baz& val) {
// prints: baz ( foo ( 1, 2 ), bar ( foo ( 3, 4 ), 5 ) )
aout(self) << to_string(make_message(val)) << endl;
set_next_behavior();
}
);
}
int main(int, char**) {
// bar has a non-trivial data member f, thus, we have to told
// announce how to serialize/deserialize this member;
// this is was the compound_member function is for;
// it takes a pointer to the non-trivial member as first argument
// followed by all "sub-members" either as member pointer or
// { getter, setter } pair
announce<bar>(compound_member(&bar::f,
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
&bar::i);
// baz has non-trivial data members with getter/setter pair
// and getter returning a mutable reference
announce<baz>(compound_member(make_pair(&baz::f, &baz::set_f),
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
// compound member that has a compound member
compound_member(&baz::b,
compound_member(&bar::f,
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
&bar::i));
// spawn a testee that receives two messages
auto t = spawn(testee, 2);
{
scoped_actor self;
self->send(t, bar{foo{1, 2}, 3});
self->send(t, baz{foo{1, 2}, bar{foo{3, 4}, 5}});
}
await_all_actors_done();
shutdown();
return 0;
// bar has a non-trivial data member f, thus, we have to told
// announce how to serialize/deserialize this member;
// this is was the compound_member function is for;
// it takes a pointer to the non-trivial member as first argument
// followed by all "sub-members" either as member pointer or
// { getter, setter } pair
announce<bar>(compound_member(&bar::f,
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
&bar::i);
// baz has non-trivial data members with getter/setter pair
// and getter returning a mutable reference
announce<baz>(compound_member(make_pair(&baz::f, &baz::set_f),
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
// compound member that has a compound member
compound_member(&baz::b,
compound_member(&bar::f,
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
&bar::i));
// spawn a testee that receives two messages
auto t = spawn(testee, 2);
{
scoped_actor self;
self->send(t, bar{foo{1, 2}, 3});
self->send(t, baz{foo{1, 2}, bar{foo{3, 4}, 5}});
}
await_all_actors_done();
shutdown();
return 0;
}
This diff is collapsed.
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011 - 2014 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the Boost Software License, Version 1.0. See *
* accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt *
******************************************************************************/
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment