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
2c1e56f5
Unverified
Commit
2c1e56f5
authored
Nov 19, 2018
by
Joseph Noir
Committed by
GitHub
Nov 19, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #795
Fix invalid class names in CAF log output
parents
28cd6b5d
c083daa0
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
439 additions
and
208 deletions
+439
-208
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+1
-0
libcaf_core/caf/detail/pretty_type_name.hpp
libcaf_core/caf/detail/pretty_type_name.hpp
+0
-15
libcaf_core/caf/logger.hpp
libcaf_core/caf/logger.hpp
+34
-27
libcaf_core/caf/string_algorithms.hpp
libcaf_core/caf/string_algorithms.hpp
+30
-82
libcaf_core/src/logger.cpp
libcaf_core/src/logger.cpp
+183
-72
libcaf_core/src/replies_to.cpp
libcaf_core/src/replies_to.cpp
+3
-4
libcaf_core/src/string_algorithms.cpp
libcaf_core/src/string_algorithms.cpp
+95
-0
libcaf_core/test/logger.cpp
libcaf_core/test/logger.cpp
+93
-8
No files found.
libcaf_core/CMakeLists.txt
View file @
2c1e56f5
...
...
@@ -106,6 +106,7 @@ set(LIBCAF_CORE_SRCS
src/stream_aborter.cpp
src/stream_manager.cpp
src/stream_priority.cpp
src/string_algorithms.cpp
src/string_view.cpp
src/stringification_inspector.cpp
src/sync_request_bouncer.cpp
...
...
libcaf_core/caf/detail/pretty_type_name.hpp
View file @
2c1e56f5
...
...
@@ -16,20 +16,6 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
// The rationale of this header is to provide a serialization API
// that is compatbile to boost.serialization. In particular, the
// design goals are:
// - allow users to integrate existing boost.serialization-based code easily
// - allow to switch out this header with the actual boost header in boost.actor
//
// Differences in semantics are:
// - CAF does *not* respect class versions
// - the `unsigned int` argument is always 0 and ignored by CAF
//
// Since CAF requires all runtime instances to have the same types
// announced, different class versions in a single actor system would
// cause inconsistencies that are not recoverable.
#pragma once
#include <string>
...
...
@@ -46,4 +32,3 @@ std::string pretty_type_name(const std::type_info& x);
}
// namespace detail
}
// namespace caf
libcaf_core/caf/logger.hpp
View file @
2c1e56f5
...
...
@@ -27,14 +27,15 @@
#include <type_traits>
#include <unordered_map>
#include "caf/fwd.hpp"
#include "caf/config.hpp"
#include "caf/unifyn.hpp"
#include "caf/type_nr.hpp"
#include "caf/timestamp.hpp"
#include "caf/ref_counted.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/fwd.hpp"
#include "caf/ref_counted.hpp"
#include "caf/string_view.hpp"
#include "caf/timestamp.hpp"
#include "caf/type_nr.hpp"
#include "caf/unifyn.hpp"
#include "caf/intrusive/drr_queue.hpp"
#include "caf/intrusive/fifo_inbox.hpp"
...
...
@@ -71,20 +72,24 @@ public:
struct
event
:
intrusive
::
singly_linked
<
event
>
{
event
()
=
default
;
event
(
int
lvl
,
const
char
*
cat
,
const
char
*
fun
,
const
char
*
fn
,
int
line
,
std
::
string
msg
,
std
::
thread
::
id
t
,
actor_id
a
,
timestamp
ts
);
event
(
int
lvl
,
string_view
cat
,
string_view
full_fun
,
string_view
fun
,
string_view
fn
,
int
line
,
std
::
string
msg
,
std
::
thread
::
id
t
,
actor_id
a
,
timestamp
ts
);
/// Level/priority of the event.
int
level
;
/// Name of the category (component) logging the event.
const
char
*
category_name
;
string_view
category_name
;
/// Name of the current context as reported by `__PRETTY_FUNCTION__`.
const
char
*
pretty_fun
;
/// Name of the current function as reported by `__PRETTY_FUNCTION__`.
string_view
pretty_fun
;
/// Name of the current function as reported by `__func__`.
string_view
simple_fun
;
/// Name of the current file.
const
char
*
file_name
;
string_view
file_name
;
/// Current line in the file.
int
line_number
;
...
...
@@ -179,6 +184,8 @@ public:
line_builder
&
operator
<<
(
const
std
::
string
&
str
);
line_builder
&
operator
<<
(
string_view
str
);
line_builder
&
operator
<<
(
const
char
*
str
);
line_builder
&
operator
<<
(
char
x
);
...
...
@@ -206,12 +213,7 @@ public:
static
logger
*
current_logger
();
bool
accepts
(
int
level
,
const
char
*
cname_begin
,
const
char
*
cname_end
);
template
<
size_t
N
>
bool
accepts
(
int
level
,
const
char
(
&
component
)[
N
])
{
return
accepts
(
level
,
component
,
component
+
N
-
1
);
}
bool
accepts
(
int
level
,
string_view
cname
);
/** @endcond */
...
...
@@ -226,10 +228,10 @@ public:
}
/// Renders the prefix (namespace and class) of a fully qualified function.
static
void
render_fun_prefix
(
std
::
ostream
&
out
,
const
char
*
pretty_fun
);
static
void
render_fun_prefix
(
std
::
ostream
&
out
,
const
event
&
x
);
/// Renders the name of a fully qualified function.
static
void
render_fun_name
(
std
::
ostream
&
out
,
const
char
*
pretty_fun
);
static
void
render_fun_name
(
std
::
ostream
&
out
,
const
event
&
x
);
/// Renders the difference between `t0` and `tn` in milliseconds.
static
void
render_time_diff
(
std
::
ostream
&
out
,
timestamp
t0
,
timestamp
tn
);
...
...
@@ -245,7 +247,7 @@ public:
static
line_format
parse_format
(
const
std
::
string
&
format_str
);
/// Skips path in `filename`.
static
const
char
*
skip_path
(
const
char
*
filename
);
static
string_view
skip_path
(
string_view
filename
);
/// Returns a string representation of the joined groups of `x` if `x` is an
/// actor with the `subscriber` mixin.
...
...
@@ -364,6 +366,14 @@ bool operator==(const logger::field& x, const logger::field& y);
#define CAF_LOG_COMPONENT "caf"
#endif // CAF_LOG_COMPONENT
#define CAF_LOG_MAKE_EVENT(aid, component, loglvl, message) \
::caf::logger::event { \
loglvl, component, CAF_PRETTY_FUN, __func__, \
caf::logger::skip_path(__FILE__), __LINE__, \
(::caf::logger::line_builder{} << message).get(), \
::std::this_thread::get_id(), aid, ::caf::make_timestamp() \
}
#if CAF_LOG_LEVEL == -1
#define CAF_LOG_IMPL(unused1, unused2, unused3)
...
...
@@ -389,12 +399,9 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
if (CAF_UNIFYN(caf_logger) != nullptr \
&& CAF_UNIFYN(caf_logger)->accepts(loglvl, component)) \
CAF_UNIFYN(caf_logger) \
->log(new ::caf::logger::event{ \
loglvl, component, CAF_PRETTY_FUN, caf::logger::skip_path(__FILE__), \
__LINE__, (::caf::logger::line_builder{} << message).get(), \
::std::this_thread::get_id(), \
CAF_UNIFYN(caf_logger)->thread_local_aid(), \
::caf::make_timestamp()}); \
->log( \
new CAF_LOG_MAKE_EVENT(CAF_UNIFYN(caf_logger)->thread_local_aid(), \
component, loglvl, message)); \
} while (false)
#define CAF_PUSH_AID(aarg) \
...
...
libcaf_core/caf/string_algorithms.hpp
View file @
2c1e56f5
...
...
@@ -28,118 +28,66 @@
#include "caf/config.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/string_view.hpp"
namespace
caf
{
// provide boost::split compatible interface
inline
st
d
::
string
is_any_of
(
std
::
string
arg
)
{
inline
st
ring_view
is_any_of
(
string_view
arg
)
{
return
arg
;
}
constexpr
bool
token_compress_on
=
false
;
template
<
class
Container
,
class
Str
,
class
Delim
>
void
split
(
Container
&
result
,
const
Str
&
str
,
const
Delim
&
delims
,
bool
keep_all
=
true
)
{
size_t
pos
=
0
;
size_t
prev
=
0
;
while
((
pos
=
str
.
find_first_of
(
delims
,
prev
))
!=
std
::
string
::
npos
)
{
if
(
pos
>
prev
)
{
auto
substr
=
str
.
substr
(
prev
,
pos
-
prev
);
if
(
!
substr
.
empty
()
||
keep_all
)
{
result
.
push_back
(
std
::
move
(
substr
));
}
}
prev
=
pos
+
1
;
}
if
(
prev
<
str
.
size
())
result
.
push_back
(
str
.
substr
(
prev
,
std
::
string
::
npos
));
}
template
<
class
Iterator
>
class
iterator_range
{
public:
using
iterator
=
Iterator
;
iterator_range
(
iterator
first
,
iterator
last
)
:
begin_
(
first
),
end_
(
last
)
{
// nop
}
void
split
(
std
::
vector
<
std
::
string
>&
result
,
string_view
str
,
string_view
delims
,
bool
keep_all
=
true
);
iterator
begin
()
const
{
return
begin_
;
}
void
split
(
std
::
vector
<
string_view
>&
result
,
string_view
str
,
string_view
delims
,
bool
keep_all
=
true
);
iterator
end
()
const
{
return
end_
;
}
void
split
(
std
::
vector
<
std
::
string
>&
result
,
string_view
str
,
char
delim
,
bool
keep_all
=
true
);
private:
iterator
begin_
;
iterator
end_
;
};
void
split
(
std
::
vector
<
string_view
>&
result
,
string_view
str
,
char
delim
,
bool
keep_all
=
true
);
template
<
class
InputIterator
>
std
::
string
join
(
InputIterator
first
,
InputIterator
last
,
string_view
glue
)
{
if
(
first
==
last
)
return
{};
std
::
ostringstream
oss
;
oss
<<
*
first
++
;
for
(;
first
!=
last
;
++
first
)
oss
<<
glue
<<
*
first
;
return
oss
.
str
();
}
template
<
class
Container
>
std
::
string
join
(
const
Container
&
c
,
const
std
::
string
&
glue
)
{
auto
begin
=
c
.
begin
();
auto
end
=
c
.
end
();
bool
first
=
true
;
std
::
ostringstream
oss
;
for
(
;
begin
!=
end
;
++
begin
)
{
if
(
first
)
first
=
false
;
else
oss
<<
glue
;
oss
<<
*
begin
;
}
return
oss
.
str
();
return
join
(
c
.
begin
(),
c
.
end
(),
glue
);
}
// end of recursion
inline
void
splice
(
std
::
string
&
,
const
std
::
string
&
)
{
inline
void
splice
(
std
::
string
&
,
string_view
)
{
// nop
}
template
<
class
T
,
class
...
Ts
>
void
splice
(
std
::
string
&
str
,
const
std
::
string
&
glue
,
T
&&
arg
,
Ts
&&
...
xs
)
{
str
+=
glue
;
void
splice
(
std
::
string
&
str
,
string_view
glue
,
T
&&
arg
,
Ts
&&
...
xs
)
{
str
.
insert
(
str
.
end
(),
glue
.
begin
(),
glue
.
end
())
;
str
+=
std
::
forward
<
T
>
(
arg
);
splice
(
str
,
glue
,
std
::
forward
<
Ts
>
(
xs
)...);
}
template
<
ptrdiff_t
WhatSize
,
ptrdiff_t
WithSize
>
void
replace_all
(
std
::
string
&
str
,
const
char
(
&
what
)[
WhatSize
],
const
char
(
&
with
)[
WithSize
])
{
// end(what) - 1 points to the null-terminator
auto
next
=
[
&
](
std
::
string
::
iterator
pos
)
->
std
::
string
::
iterator
{
return
std
::
search
(
pos
,
str
.
end
(),
std
::
begin
(
what
),
std
::
end
(
what
)
-
1
);
};
auto
i
=
next
(
std
::
begin
(
str
));
while
(
i
!=
std
::
end
(
str
))
{
auto
before
=
std
::
distance
(
std
::
begin
(
str
),
i
);
CAF_ASSERT
(
before
>=
0
);
str
.
replace
(
i
,
i
+
WhatSize
-
1
,
with
);
// i became invalidated -> use new iterator pointing
// to the first character after the replaced text
i
=
next
(
str
.
begin
()
+
before
+
(
WithSize
-
1
));
}
}
/// Replaces all occurrences of `what` by `with` in `str`.
void
replace_all
(
std
::
string
&
str
,
string_view
what
,
string_view
with
);
template
<
size_t
S
>
bool
starts_with
(
const
std
::
string
&
str
,
const
char
(
&
prefix
)[
S
])
{
return
str
.
compare
(
0
,
S
-
1
,
prefix
)
==
0
;
}
/// Returns whether `str` begins with `prefix`.
bool
starts_with
(
string_view
str
,
string_view
prefix
);
template
<
size_t
S
>
bool
ends_with
(
const
std
::
string
&
str
,
const
char
(
&
suffix
)[
S
])
{
auto
n
=
str
.
size
();
auto
m
=
S
-
1
;
if
(
n
>=
m
)
return
str
.
compare
(
n
-
m
,
m
,
suffix
)
==
0
;
return
false
;
}
/// Returns whether `str` ends with `suffix`.
bool
ends_with
(
string_view
str
,
string_view
suffix
);
template
<
class
T
>
typename
std
::
enable_if
<
...
...
libcaf_core/src/logger.cpp
View file @
2c1e56f5
This diff is collapsed.
Click to expand it.
libcaf_core/src/replies_to.cpp
View file @
2c1e56f5
...
...
@@ -25,16 +25,15 @@ std::string replies_to_type_name(size_t input_size,
const
std
::
string
*
input
,
size_t
output_opt1_size
,
const
std
::
string
*
output_opt1
)
{
using
irange
=
iterator_range
<
const
std
::
string
*>
;
std
::
string
glue
=
","
;
string_view
glue
=
","
;
std
::
string
result
;
// 'void' is not an announced type, hence we check whether uniform_typeid
// did return a valid pointer to identify 'void' (this has the
// possibility of false positives, but those will be catched anyways)
result
=
"caf::replies_to<"
;
result
+=
join
(
i
range
{
input
,
input
+
input_size
}
,
glue
);
result
+=
join
(
i
nput
,
input
+
input_size
,
glue
);
result
+=
">::with<"
;
result
+=
join
(
irange
{
output_opt1
,
output_opt1
+
output_opt1_size
}
,
glue
);
result
+=
join
(
output_opt1
,
output_opt1
+
output_opt1_size
,
glue
);
result
+=
">"
;
return
result
;
}
...
...
libcaf_core/src/string_algorithms.cpp
0 → 100644
View file @
2c1e56f5
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/string_algorithms.hpp"
namespace
caf
{
namespace
{
template
<
class
F
>
void
split_impl
(
F
consume
,
string_view
str
,
string_view
delims
,
bool
keep_all
)
{
size_t
pos
=
0
;
size_t
prev
=
0
;
while
((
pos
=
str
.
find_first_of
(
delims
,
prev
))
!=
std
::
string
::
npos
)
{
if
(
pos
>
prev
)
{
auto
substr
=
str
.
substr
(
prev
,
pos
-
prev
);
if
(
!
substr
.
empty
()
||
keep_all
)
consume
(
substr
);
}
prev
=
pos
+
1
;
}
if
(
prev
<
str
.
size
())
consume
(
str
.
substr
(
prev
,
std
::
string
::
npos
));
}
}
// namespace <anonymous>
void
split
(
std
::
vector
<
std
::
string
>&
result
,
string_view
str
,
string_view
delims
,
bool
keep_all
)
{
auto
f
=
[
&
](
string_view
sv
)
{
result
.
emplace_back
(
sv
.
begin
(),
sv
.
end
());
};
return
split_impl
(
f
,
str
,
delims
,
keep_all
);
}
void
split
(
std
::
vector
<
string_view
>&
result
,
string_view
str
,
string_view
delims
,
bool
keep_all
)
{
auto
f
=
[
&
](
string_view
sv
)
{
result
.
emplace_back
(
sv
);
};
return
split_impl
(
f
,
str
,
delims
,
keep_all
);
}
void
split
(
std
::
vector
<
std
::
string
>&
result
,
string_view
str
,
char
delim
,
bool
keep_all
)
{
split
(
result
,
str
,
string_view
{
&
delim
,
1
},
keep_all
);
}
void
split
(
std
::
vector
<
string_view
>&
result
,
string_view
str
,
char
delim
,
bool
keep_all
)
{
split
(
result
,
str
,
string_view
{
&
delim
,
1
},
keep_all
);
}
void
replace_all
(
std
::
string
&
str
,
string_view
what
,
string_view
with
)
{
// end(what) - 1 points to the null-terminator
auto
next
=
[
&
](
std
::
string
::
iterator
pos
)
->
std
::
string
::
iterator
{
return
std
::
search
(
pos
,
str
.
end
(),
what
.
begin
(),
what
.
end
());
};
auto
i
=
next
(
str
.
begin
());
while
(
i
!=
str
.
end
())
{
auto
before
=
std
::
distance
(
str
.
begin
(),
i
);
CAF_ASSERT
(
before
>=
0
);
str
.
replace
(
i
,
i
+
what
.
size
(),
with
.
begin
(),
with
.
end
());
// Iterator i became invalidated -> use new iterator pointing to the first
// character after the replaced text.
i
=
next
(
str
.
begin
()
+
before
+
with
.
size
());
}
}
bool
starts_with
(
string_view
str
,
string_view
prefix
)
{
return
str
.
compare
(
0
,
prefix
.
size
(),
prefix
)
==
0
;
}
bool
ends_with
(
string_view
str
,
string_view
suffix
)
{
auto
n
=
str
.
size
();
auto
m
=
suffix
.
size
();
return
n
>=
m
?
str
.
compare
(
n
-
m
,
m
,
suffix
)
==
0
:
false
;
}
}
// namespace caf
libcaf_core/test/logger.cpp
View file @
2c1e56f5
...
...
@@ -27,9 +27,35 @@
#include "caf/all.hpp"
using
namespace
caf
;
using
namespace
std
;
using
namespace
std
::
chrono
;
using
std
::
string
;
#define CHECK_FUN_PREFIX(PrefixName) \
do { \
auto e = CAF_LOG_MAKE_EVENT(0, "caf", CAF_LOG_LEVEL_DEBUG, ""); \
std::ostringstream oss; \
::caf::logger::render_fun_prefix(oss, e); \
auto prefix = oss.str(); \
if (prefix != PrefixName) \
CAF_ERROR("rendering the prefix of " << e.pretty_fun << " produced " \
<< prefix << " instead of " \
<< PrefixName); \
else \
CAF_CHECK_EQUAL(prefix, PrefixName); \
} while (false)
void
global_fun
()
{
CHECK_FUN_PREFIX
(
"GLOBAL"
);
}
// Little helper that allows us to write a single check for all compilers.
// Clang expands template parameters in __PRETTY_FUNCTION__, while GCC does
// not. For example, Clang would produce "void foo<int>::bar()", while GCC
// would produce "void foo<T>::bar() [with T = int]". A type called T gives
// us always the same ouptut for the prefix.
struct
T
{};
namespace
{
struct
fixture
{
...
...
@@ -48,8 +74,8 @@ struct fixture {
template
<
class
F
,
class
...
Ts
>
string
render
(
F
f
,
Ts
&&
...
xs
)
{
ostringstream
oss
;
f
(
oss
,
forward
<
Ts
>
(
xs
)...);
std
::
ostringstream
oss
;
f
(
oss
,
std
::
forward
<
Ts
>
(
xs
)...);
return
oss
.
str
();
}
...
...
@@ -57,6 +83,52 @@ struct fixture {
logger
::
line_format
lf
;
};
void
fun
()
{
CHECK_FUN_PREFIX
(
"$"
);
}
const
char
*
ptr_fun
(
const
char
*
x
)
{
CHECK_FUN_PREFIX
(
"$"
);
return
x
;
}
const
int
&
ref_fun
(
const
int
&
x
)
{
CHECK_FUN_PREFIX
(
"$"
);
return
x
;
}
template
<
class
T
>
struct
tpl
{
static
void
run
()
{
CHECK_FUN_PREFIX
(
"$.tpl<T>"
);
}
};
namespace
foo
{
void
fun
()
{
CHECK_FUN_PREFIX
(
"$.foo"
);
}
const
char
*
ptr_fun
(
const
char
*
x
)
{
CHECK_FUN_PREFIX
(
"$.foo"
);
return
x
;
}
const
int
&
ref_fun
(
const
int
&
x
)
{
CHECK_FUN_PREFIX
(
"$.foo"
);
return
x
;
}
template
<
class
T
>
struct
tpl
{
static
void
run
()
{
CHECK_FUN_PREFIX
(
"$.foo.tpl<T>"
);
}
};
}
// namespace foo
constexpr
const
char
*
file_format
=
"%r %c %p %a %t %C %M %F:%L %m%n"
;
}
// namespace <anonymous>
...
...
@@ -95,10 +167,6 @@ CAF_TEST(parse_default_format_strings) {
}
CAF_TEST
(
rendering
)
{
// Rendering of type names and function names.
const
char
*
foobar
=
"void ns::foo::bar()"
;
CAF_CHECK_EQUAL
(
render
(
logger
::
render_fun_name
,
foobar
),
"bar"
);
CAF_CHECK_EQUAL
(
render
(
logger
::
render_fun_prefix
,
foobar
),
"ns.foo"
);
// Rendering of time points.
timestamp
t0
;
timestamp
t1
{
timestamp
::
duration
{
5000000
}};
// epoch + 5000000ns (5ms)
...
...
@@ -113,13 +181,17 @@ CAF_TEST(rendering) {
CAF_LOG_LEVEL_WARNING
,
"unit.test"
,
"void ns::foo::bar()"
,
"bar"
,
"foo.cpp"
,
42
,
"hello world"
,
this_thread
::
get_id
(),
std
::
this_thread
::
get_id
(),
0
,
t0
};
CAF_CHECK_EQUAL
(
render
(
logger
::
render_fun_name
,
e
),
string_view
{
"bar"
});
CAF_CHECK_EQUAL
(
render
(
logger
::
render_fun_prefix
,
e
),
string_view
{
"ns.foo"
});
// Exclude %r and %t from rendering test because they are nondeterministic.
actor_system
sys
{
cfg
};
auto
lf
=
logger
::
parse_format
(
"%c %p %a %C %M %F:%L %m"
);
...
...
@@ -130,4 +202,17 @@ CAF_TEST(rendering) {
"unit.test WARN actor0 ns.foo bar foo.cpp:42 hello world"
);
}
CAF_TEST
(
render_fun_prefix
)
{
int
i
=
42
;
global_fun
();
fun
();
ptr_fun
(
nullptr
);
ref_fun
(
i
);
tpl
<
T
>::
run
();
foo
::
fun
();
foo
::
ptr_fun
(
nullptr
);
foo
::
ref_fun
(
i
);
foo
::
tpl
<
T
>::
run
();
}
CAF_TEST_FIXTURE_SCOPE_END
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment