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
1f25d8e8
Unverified
Commit
1f25d8e8
authored
Sep 16, 2019
by
Dominik Charousset
Committed by
GitHub
Sep 16, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #915
Fix several warnings on GCC and Clang
parents
77cf8948
69727629
Changes
17
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
131 additions
and
113 deletions
+131
-113
libcaf_core/caf/data_processor.hpp
libcaf_core/caf/data_processor.hpp
+2
-2
libcaf_core/caf/detail/ini_consumer.hpp
libcaf_core/caf/detail/ini_consumer.hpp
+0
-2
libcaf_core/caf/detail/parser/add_ascii.hpp
libcaf_core/caf/detail/parser/add_ascii.hpp
+2
-3
libcaf_core/caf/detail/parser/sub_ascii.hpp
libcaf_core/caf/detail/parser/sub_ascii.hpp
+3
-5
libcaf_core/caf/typed_behavior.hpp
libcaf_core/caf/typed_behavior.hpp
+38
-46
libcaf_core/src/fnv_hash.cpp
libcaf_core/src/fnv_hash.cpp
+16
-15
libcaf_core/src/node_id.cpp
libcaf_core/src/node_id.cpp
+1
-1
libcaf_core/test/inspector.cpp
libcaf_core/test/inspector.cpp
+34
-7
libcaf_core/test/match.cpp
libcaf_core/test/match.cpp
+1
-1
libcaf_core/test/serialization.cpp
libcaf_core/test/serialization.cpp
+1
-1
libcaf_core/test/thread_hook.cpp
libcaf_core/test/thread_hook.cpp
+0
-2
libcaf_io/caf/io/network/event_handler.hpp
libcaf_io/caf/io/network/event_handler.hpp
+1
-1
libcaf_io/caf/io/receive_policy.hpp
libcaf_io/caf/io/receive_policy.hpp
+7
-3
libcaf_io/src/event_handler.cpp
libcaf_io/src/event_handler.cpp
+5
-4
libcaf_io/src/stream.cpp
libcaf_io/src/stream.cpp
+1
-1
libcaf_io/test/typed_broker.cpp
libcaf_io/test/typed_broker.cpp
+2
-2
libcaf_openssl/src/session.cpp
libcaf_openssl/src/session.cpp
+17
-17
No files found.
libcaf_core/caf/data_processor.hpp
View file @
1f25d8e8
...
...
@@ -176,7 +176,7 @@ public:
lhs
=
static_cast
<
underlying
>
(
rhs
);
}
}
assign
;
underlying
tmp
;
underlying
tmp
=
0
;
return
convert_apply
(
dref
(),
x
,
tmp
,
assign
);
}
...
...
@@ -227,7 +227,7 @@ public:
lhs
.
resize
((
rhs
.
size
()
-
1
)
/
8
+
1
,
0
);
for
(
bool
b
:
rhs
)
{
if
(
b
)
lhs
[
k
/
8
]
|=
(
1
<<
(
k
%
8
));
lhs
[
k
/
8
]
|=
static_cast
<
uint8_t
>
(
1
<<
(
k
%
8
));
++
k
;
}
}
...
...
libcaf_core/caf/detail/ini_consumer.hpp
View file @
1f25d8e8
...
...
@@ -194,8 +194,6 @@ public:
ini_consumer
(
const
config_option_set
&
options
,
settings
&
cfg
);
ini_consumer
(
ini_consumer
&&
)
=
default
;
// -- properties -------------------------------------------------------------
ini_category_consumer
begin_map
();
...
...
libcaf_core/caf/detail/parser/add_ascii.hpp
View file @
1f25d8e8
...
...
@@ -37,12 +37,12 @@ bool add_ascii(T& x, char c, enable_if_tt<std::is_integral<T>, int> u = 0) {
CAF_IGNORE_UNUSED
(
u
);
if
(
x
>
(
std
::
numeric_limits
<
T
>::
max
()
/
Base
))
return
false
;
x
*=
Base
;
x
*=
static_cast
<
T
>
(
Base
)
;
ascii_to_int
<
Base
,
T
>
f
;
auto
y
=
f
(
c
);
if
(
x
>
(
std
::
numeric_limits
<
T
>::
max
()
-
y
))
return
false
;
x
+=
y
;
x
+=
static_cast
<
T
>
(
y
)
;
return
true
;
}
...
...
@@ -58,4 +58,3 @@ bool add_ascii(T& x, char c,
}
// namespace parser
}
// namespace detail
}
// namespace caf
libcaf_core/caf/detail/parser/sub_ascii.hpp
View file @
1f25d8e8
...
...
@@ -37,12 +37,12 @@ bool sub_ascii(T& x, char c, enable_if_tt<std::is_integral<T>, int> u = 0) {
CAF_IGNORE_UNUSED
(
u
);
if
(
x
<
(
std
::
numeric_limits
<
T
>::
min
()
/
Base
))
return
false
;
x
*=
Base
;
x
*=
static_cast
<
T
>
(
Base
)
;
ascii_to_int
<
Base
,
T
>
f
;
auto
y
=
f
(
c
);
if
(
x
<
(
std
::
numeric_limits
<
T
>::
min
()
+
y
))
return
false
;
x
-=
y
;
x
-=
static_cast
<
T
>
(
y
)
;
return
true
;
}
...
...
@@ -51,12 +51,10 @@ bool sub_ascii(T& x, char c,
enable_if_tt
<
std
::
is_floating_point
<
T
>
,
int
>
u
=
0
)
{
CAF_IGNORE_UNUSED
(
u
);
ascii_to_int
<
Base
,
T
>
f
;
x
=
(
x
*
Base
)
-
f
(
c
);
x
=
static_cast
<
T
>
((
x
*
Base
)
-
f
(
c
)
);
return
true
;
}
}
// namespace parser
}
// namespace detail
}
// namespace caf
libcaf_core/caf/typed_behavior.hpp
View file @
1f25d8e8
...
...
@@ -20,9 +20,9 @@
#include "caf/behavior.hpp"
#include "caf/deduce_mpi.hpp"
#include "caf/interface_mismatch.hpp"
#include "caf/message_handler.hpp"
#include "caf/system_messages.hpp"
#include "caf/interface_mismatch.hpp"
#include "caf/detail/typed_actor_util.hpp"
...
...
@@ -48,9 +48,11 @@ struct same_input : std::is_same<Input, typename RepliesToWith::input_types> {};
template
<
class
Output
,
class
RepliesToWith
>
struct
same_output_or_skip_t
{
using
other
=
typename
RepliesToWith
::
output_types
;
static
constexpr
bool
value
=
std
::
is_same
<
Output
,
typename
RepliesToWith
::
output_types
>::
value
||
std
::
is_same
<
Output
,
type_list
<
skip_t
>>::
value
;
static
constexpr
bool
value
=
std
::
is_same
<
Output
,
typename
RepliesToWith
::
output_types
>::
value
||
std
::
is_same
<
Output
,
type_list
<
skip_t
>>::
value
;
};
template
<
class
SList
>
...
...
@@ -60,35 +62,31 @@ struct valid_input_predicate {
using
input_types
=
typename
Expr
::
input_types
;
using
output_types
=
typename
Expr
::
output_types
;
// get matching elements for input type
using
filtered_slist
=
typename
tl_filter
<
SList
,
tbind
<
same_input
,
input_types
>::
template
type
>
::
type
;
using
filtered_slist
=
typename
tl_filter
<
SList
,
tbind
<
same_input
,
input_types
>::
template
type
>
::
type
;
static_assert
(
tl_size
<
filtered_slist
>::
value
>
0
,
"cannot assign given match expression to "
"typed behavior, because the expression "
"contains at least one pattern that is "
"not defined in the actor's type"
);
static
constexpr
bool
value
=
tl_exists
<
filtered_slist
,
tbind
<
same_output_or_skip_t
,
output_types
>::
template
type
>
::
value
;
filtered_slist
,
tbind
<
same_output_or_skip_t
,
output_types
>::
template
type
>
::
value
;
// check whether given output matches in the filtered list
static_assert
(
value
,
"cannot assign given match expression to "
static_assert
(
value
,
"cannot assign given match expression to "
"typed behavior, because at least one return "
"type does not match"
);
};
};
template
<
class
T
>
struct
is_system_msg_handler
:
std
::
false_type
{
};
struct
is_system_msg_handler
:
std
::
false_type
{};
template
<
>
struct
is_system_msg_handler
<
reacts_to
<
exit_msg
>>
:
std
::
true_type
{
};
struct
is_system_msg_handler
<
reacts_to
<
exit_msg
>>
:
std
::
true_type
{};
template
<
>
struct
is_system_msg_handler
<
reacts_to
<
down_msg
>>
:
std
::
true_type
{
};
struct
is_system_msg_handler
<
reacts_to
<
down_msg
>>
:
std
::
true_type
{};
// Tests whether the input list (IList) matches the
// signature list (SList) for a typed actor behavior
...
...
@@ -96,28 +94,22 @@ template <class SList, class IList>
struct
valid_input
{
// strip exit_msg and down_msg from input types,
// because they're always allowed
using
adjusted_slist
=
typename
tl_filter_not
<
SList
,
is_system_msg_handler
>::
type
;
using
adjusted_ilist
=
typename
tl_filter_not
<
IList
,
is_system_msg_handler
>::
type
;
using
adjusted_slist
=
typename
tl_filter_not
<
SList
,
is_system_msg_handler
>::
type
;
using
adjusted_ilist
=
typename
tl_filter_not
<
IList
,
is_system_msg_handler
>::
type
;
// check for each element in IList that there's an element in SList that
// (1) has an identical input type list
// (2) has an identical output type list
// OR the output of the element in IList is skip_t
static_assert
(
detail
::
tl_is_distinct
<
IList
>::
value
,
"given pattern is not distinct"
);
static
constexpr
bool
value
=
tl_size
<
adjusted_slist
>::
value
==
tl_size
<
adjusted_ilist
>::
value
static
constexpr
bool
value
=
tl_size
<
adjusted_slist
>::
value
==
tl_size
<
adjusted_ilist
>::
value
&&
tl_forall
<
adjusted_ilist
,
valid_input_predicate
<
adjusted_slist
>::
template
inner
>
::
value
;
valid_input_predicate
<
adjusted_slist
>::
template
inner
>
::
value
;
};
// this function is called from typed_behavior<...>::set and its whole
...
...
@@ -130,8 +122,7 @@ void static_check_typed_behavior_input() {
// InputList if its return type is identical to all "missing"
// input types ... however, it might lead to unexpected results
// and would cause a lot of not-so-straightforward code here
static_assert
(
is_valid
,
"given pattern cannot be used to initialize "
static_assert
(
is_valid
,
"given pattern cannot be used to initialize "
"typed behavior (exact match needed)"
);
}
...
...
@@ -165,7 +156,7 @@ public:
using
signatures
=
detail
::
type_list
<
Sigs
...
>
;
/// Empty struct tag for constructing from an untyped behavior.
struct
unsafe_init
{
};
struct
unsafe_init
{};
// -- constructors, destructors, and assignment operators --------------------
...
...
@@ -179,8 +170,9 @@ public:
using
other_signatures
=
detail
::
type_list
<
Ts
...
>
;
using
m
=
interface_mismatch_t
<
other_signatures
,
signatures
>
;
// trigger static assert on mismatch
detail
::
static_error_printer
<
sizeof
...(
Ts
),
m
::
value
,
typename
m
::
xs
,
typename
m
::
ys
>
guard
;
detail
::
static_error_printer
<
static_cast
<
int
>
(
sizeof
...(
Ts
)),
m
::
value
,
typename
m
::
xs
,
typename
m
::
ys
>
guard
;
CAF_IGNORE_UNUSED
(
guard
);
}
...
...
@@ -200,7 +192,7 @@ public:
// -- modifiers --------------------------------------------------------------
/// Exchanges the contents of this and other.
inline
void
swap
(
typed_behavior
&
other
)
{
void
swap
(
typed_behavior
&
other
)
{
bhvr_
.
swap
(
other
.
bhvr_
);
}
...
...
@@ -242,8 +234,9 @@ private:
using
found_signatures
=
detail
::
type_list
<
deduce_mpi_t
<
Ts
>
...
>
;
using
m
=
interface_mismatch_t
<
found_signatures
,
signatures
>
;
// trigger static assert on mismatch
detail
::
static_error_printer
<
sizeof
...(
Ts
),
m
::
value
,
typename
m
::
xs
,
typename
m
::
ys
>
guard
;
detail
::
static_error_printer
<
static_cast
<
int
>
(
sizeof
...(
Ts
)),
m
::
value
,
typename
m
::
xs
,
typename
m
::
ys
>
guard
;
CAF_IGNORE_UNUSED
(
guard
);
// final (type-erasure) step
intrusive_ptr
<
detail
::
behavior_impl
>
ptr
=
std
::
move
(
bp
);
...
...
@@ -254,10 +247,9 @@ private:
};
template
<
class
T
>
struct
is_typed_behavior
:
std
::
false_type
{
};
struct
is_typed_behavior
:
std
::
false_type
{};
template
<
class
...
Sigs
>
struct
is_typed_behavior
<
typed_behavior
<
Sigs
...
>>
:
std
::
true_type
{
};
struct
is_typed_behavior
<
typed_behavior
<
Sigs
...
>>
:
std
::
true_type
{};
}
// namespace caf
libcaf_core/src/fnv_hash.cpp
View file @
1f25d8e8
...
...
@@ -18,41 +18,42 @@
#include "caf/detail/fnv_hash.hpp"
#include <cstdint>
namespace
caf
{
namespace
detail
{
namespace
{
template
<
size_t
IntegerSize
>
struct
hash_conf_helper
;
#if SIZE_MAX == 0xFFFFFFFF
constexpr
size_t
basis
=
2166136261u
;
constexpr
size_t
prime
=
16777619u
;
#elif SIZE_MAX == 0xFFFFFFFFFFFFFFFF
template
<
>
struct
hash_conf_helper
<
4
>
{
static
constexpr
size_t
basis
=
2166136261u
;
constexpr
size_t
basis
=
14695981039346656037u
;
static
constexpr
size_t
prime
=
16777619u
;
};
constexpr
size_t
prime
=
1099511628211u
;
template
<
>
struct
hash_conf_helper
<
8
>
{
static
constexpr
size_t
basis
=
14695981039346656037u
;
#else
static
constexpr
size_t
prime
=
1099511628211u
;
};
# error Platform and/or compiler not supported
struct
hash_conf
:
hash_conf_helper
<
sizeof
(
size_t
)
>
{};
#endif
}
// namespace
size_t
fnv_hash
(
const
unsigned
char
*
first
,
const
unsigned
char
*
last
)
{
return
fnv_hash_append
(
hash_conf
::
basis
,
first
,
last
);
return
fnv_hash_append
(
basis
,
first
,
last
);
}
size_t
fnv_hash_append
(
size_t
intermediate
,
const
unsigned
char
*
first
,
const
unsigned
char
*
last
)
{
auto
result
=
intermediate
;
for
(;
first
!=
last
;
++
first
)
{
result
*=
hash_conf
::
prime
;
result
*=
prime
;
result
^=
*
first
;
}
return
result
;
...
...
libcaf_core/src/node_id.cpp
View file @
1f25d8e8
...
...
@@ -204,7 +204,7 @@ error node_id::serialize(serializer& sink) const {
}
error
node_id
::
deserialize
(
deserializer
&
source
)
{
a
tom_value
impl
;
a
uto
impl
=
static_cast
<
atom_value
>
(
0
)
;
if
(
auto
err
=
source
(
impl
))
return
err
;
if
(
impl
==
atom
(
""
))
{
...
...
libcaf_core/test/inspector.cpp
View file @
1f25d8e8
...
...
@@ -228,22 +228,49 @@ CAF_TEST(stringification_inspector) {
}
namespace
{
template
<
class
T
>
struct
is_integral_or_enum
{
static
constexpr
bool
value
=
std
::
is_integral
<
T
>::
value
||
std
::
is_enum
<
T
>::
value
;
};
struct
binary_serialization_policy
{
execution_unit
&
context
;
template
<
class
T
>
bool
operator
()(
T
&
x
)
{
std
::
vector
<
char
>
buf
;
binary_serializer
f
{
&
context
,
buf
};
f
(
x
);
binary_deserializer
g
{
&
context
,
buf
};
std
::
vector
<
char
>
to_buf
(
const
T
&
x
)
{
std
::
vector
<
char
>
result
;
binary_serializer
sink
{
&
context
,
result
};
if
(
auto
err
=
sink
(
x
))
CAF_FAIL
(
"failed to serialize "
<<
x
<<
": "
<<
err
);
return
result
;
}
template
<
class
T
>
detail
::
enable_if_t
<
is_integral_or_enum
<
T
>::
value
,
bool
>
operator
()(
T
&
x
)
{
auto
buf
=
to_buf
(
x
);
binary_deserializer
source
{
&
context
,
buf
};
auto
y
=
static_cast
<
T
>
(
0
);
if
(
auto
err
=
source
(
y
))
CAF_FAIL
(
"failed to deserialize from buffer: "
<<
err
);
CAF_CHECK_EQUAL
(
x
,
y
);
return
detail
::
safe_equal
(
x
,
y
);
}
template
<
class
T
>
detail
::
enable_if_t
<!
is_integral_or_enum
<
T
>::
value
,
bool
>
operator
()(
T
&
x
)
{
auto
buf
=
to_buf
(
x
);
binary_deserializer
source
{
&
context
,
buf
};
T
y
;
g
(
y
);
if
(
auto
err
=
source
(
y
))
CAF_FAIL
(
"failed to deserialize from buffer: "
<<
err
);
CAF_CHECK_EQUAL
(
x
,
y
);
return
detail
::
safe_equal
(
x
,
y
);
}
};
}
// namespace <anonymous>
}
// namespace
CAF_TEST
(
binary_serialization_inspectors
)
{
actor_system_config
cfg
;
...
...
libcaf_core/test/match.cpp
View file @
1f25d8e8
...
...
@@ -95,7 +95,7 @@ struct fixture {
auto
last
=
end
(
invoked
);
auto
i
=
find
(
first
,
last
,
true
);
if
(
i
!=
last
)
{
CAF_REQUIRE
_EQUAL
(
count
(
i
,
last
,
true
),
1u
);
CAF_REQUIRE
(
count
(
i
,
last
,
true
)
==
1
);
return
distance
(
first
,
i
);
}
return
-
1
;
...
...
libcaf_core/test/serialization.cpp
View file @
1f25d8e8
...
...
@@ -240,6 +240,7 @@ struct is_message {
}
// namespace <anonymous>
#define SERIALIZATION_TEST(name) \
namespace { \
template <class Serializer, class Deserializer> \
struct name##_tpl : fixture<Serializer, Deserializer> { \
using super = fixture<Serializer, Deserializer>; \
...
...
@@ -263,7 +264,6 @@ struct is_message {
using super::msg_roundtrip; \
void run_test_impl(); \
}; \
namespace { \
using name##_binary = name##_tpl<binary_serializer, binary_deserializer>; \
using name##_stream = name##_tpl<stream_serializer<vectorbuf>, \
stream_deserializer<charbuf>>; \
...
...
libcaf_core/test/thread_hook.cpp
View file @
1f25d8e8
...
...
@@ -33,8 +33,6 @@ using atomic_count = std::atomic<size_t>;
size_t
assumed_thread_count
;
size_t
assumed_init_calls
;
std
::
mutex
mx
;
struct
dummy_thread_hook
:
thread_hook
{
void
init
(
actor_system
&
)
override
{
// nop
...
...
libcaf_io/caf/io/network/event_handler.hpp
View file @
1f25d8e8
...
...
@@ -45,7 +45,7 @@ public:
bool
shutting_down
:
1
;
/// Stores what receive policy is currently active.
receive_policy_flag
rd_flag
:
2
;
unsigned
rd_flag
:
2
;
};
event_handler
(
default_multiplexer
&
dm
,
native_socket
sockfd
);
...
...
libcaf_io/caf/io/receive_policy.hpp
View file @
1f25d8e8
...
...
@@ -33,6 +33,10 @@ enum class receive_policy_flag : unsigned {
exactly
};
constexpr
unsigned
to_integer
(
receive_policy_flag
x
)
{
return
static_cast
<
unsigned
>
(
x
);
}
inline
std
::
string
to_string
(
receive_policy_flag
x
)
{
return
x
==
receive_policy_flag
::
at_least
?
"at_least"
...
...
@@ -45,17 +49,17 @@ public:
using
config
=
std
::
pair
<
receive_policy_flag
,
size_t
>
;
static
inline
config
at_least
(
size_t
num_bytes
)
{
static
config
at_least
(
size_t
num_bytes
)
{
CAF_ASSERT
(
num_bytes
>
0
);
return
{
receive_policy_flag
::
at_least
,
num_bytes
};
}
static
inline
config
at_most
(
size_t
num_bytes
)
{
static
config
at_most
(
size_t
num_bytes
)
{
CAF_ASSERT
(
num_bytes
>
0
);
return
{
receive_policy_flag
::
at_most
,
num_bytes
};
}
static
inline
config
exactly
(
size_t
num_bytes
)
{
static
config
exactly
(
size_t
num_bytes
)
{
CAF_ASSERT
(
num_bytes
>
0
);
return
{
receive_policy_flag
::
exactly
,
num_bytes
};
}
...
...
libcaf_io/src/event_handler.cpp
View file @
1f25d8e8
...
...
@@ -34,7 +34,8 @@ namespace network {
event_handler
::
event_handler
(
default_multiplexer
&
dm
,
native_socket
sockfd
)
:
fd_
(
sockfd
),
state_
{
true
,
false
,
false
,
false
,
receive_policy_flag
::
at_least
},
state_
{
true
,
false
,
false
,
false
,
to_integer
(
receive_policy_flag
::
at_least
)},
eventbf_
(
0
),
backend_
(
dm
)
{
set_fd_flags
();
...
...
libcaf_io/src/stream.cpp
View file @
1f25d8e8
...
...
@@ -55,7 +55,7 @@ void stream::activate(stream_manager* mgr) {
}
void
stream
::
configure_read
(
receive_policy
::
config
config
)
{
state_
.
rd_flag
=
config
.
first
;
state_
.
rd_flag
=
to_integer
(
config
.
first
)
;
max_
=
config
.
second
;
}
...
...
libcaf_io/test/typed_broker.cpp
View file @
1f25d8e8
...
...
@@ -120,8 +120,8 @@ peer::behavior_type peer_fun(peer::broker_pointer self, connection_handle hdl,
},
[
=
](
const
new_data_msg
&
msg
)
{
CAF_MESSAGE
(
"received new_data_msg"
);
a
tom_value
x
;
int
y
;
a
uto
x
=
static_cast
<
atom_value
>
(
0
)
;
auto
y
=
0
;
binary_deserializer
source
{
self
->
system
(),
msg
.
buf
};
auto
e
=
source
(
x
,
y
);
CAF_REQUIRE
(
!
e
);
...
...
libcaf_openssl/src/session.cpp
View file @
1f25d8e8
...
...
@@ -35,7 +35,7 @@ CAF_POP_WARNINGS
#include "caf/detail/scope_guard.hpp"
#include <signal.h>
#
define CAF_BLOCK_SIGPIPE()
\
#
define CAF_BLOCK_SIGPIPE()
\
sigset_t sigpipe_mask; \
sigemptyset(&sigpipe_mask); \
sigaddset(&sigpipe_mask, SIGPIPE); \
...
...
@@ -45,7 +45,7 @@ CAF_POP_WARNINGS
exit(1); \
} \
auto sigpipe_restore_guard = ::caf::detail::make_scope_guard([&] { \
struct timespec zerotime = {0};
\
struct timespec zerotime = {};
\
sigtimedwait(&sigpipe_mask, 0, &zerotime); \
if (pthread_sigmask(SIG_SETMASK, &saved_mask, 0) == -1) { \
perror("pthread_sigmask"); \
...
...
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