Flow 1.0.0
Flow project: Full implementation reference.
Public Types | Public Member Functions | Private Attributes | List of all members
flow::log::Component Class Reference

A light-weight class, each object storing a component payload encoding an enum value from enum type of user's choice, and a light-weight ID of that enum type itself. More...

#include <log.hpp>

Collaboration diagram for flow::log::Component:
[legend]

Public Types

using enum_raw_t = unsigned int
 The type Payload must be enum class Payload : enum_raw_t: an enum type encoded via this integer type. More...
 

Public Member Functions

 Component ()
 Constructs a Component that stores no payload; meaning an unspecified-component Component that returns empty() == true. More...
 
template<typename Payload >
 Component (Payload payload)
 Constructs a Component with the given payload of arbitrary type, so long as that type is an enum class : Component::enum_raw_t. More...
 
 Component (const Component &src)
 Copies the source Component into *this. More...
 
 Component (Component &&src_moved)
 Constructs *this equal to src_moved. More...
 
Componentoperator= (const Component &src)
 Overwrites *this with a copy of src. More...
 
template<typename Payload >
Componentoperator= (Payload new_payload)
 Equivalent to operator=(Component<Payload>(new_payload)) modulo possibly minor perf differences. More...
 
Componentoperator= (Component &&src_moved)
 Makes *this equal to src_moved. More...
 
bool empty () const
 Returns true if *this is as if default-constructed (a null Component); false if as if constructed via the 1-arg constructor (a non-null Component). More...
 
template<typename Payload >
Payload payload () const
 Returns reference to immutable payload stored in *this; undefined behavior if empty() == true. More...
 
const std::type_info & payload_type () const
 Returns typeid(Payload), where Payload was the template param used when calling the originating one-arg constructor or equivalent; undefined behavior if empty() == true. More...
 
std::type_index payload_type_index () const
 Convenience accessor that returns std::type_index(payload_type()), which can be used most excellently to store things in associative containers (like std::map) keyed by disparate Component payload enum types. More...
 
enum_raw_t payload_enum_raw_value () const
 Returns the numeric value of the enum payload stored by this Component, originating in the one-arg constructor; undefined behavior if empty() == true. More...
 

Private Attributes

std::type_info const * m_payload_type_or_null
 The typeid() of the Payload passed to the 1-arg constructor; if 0-arg ctor was used (empty() is true) then it is null. More...
 
enum_raw_t m_payload_enum_raw_value
 The internally stored integer representation of the enum value passed to the 1-arg constructor; meaningless and ignored if 0-arg ctor was used (empty() is true). More...
 

Detailed Description

A light-weight class, each object storing a component payload encoding an enum value from enum type of user's choice, and a light-weight ID of that enum type itself.

A Component is supplied by the user, at every logging call site, along with the message. Log_context (or FLOW_LOG_SET_CONTEXT() in relatively rare scenarios) makes this easier, so typically user need not literally type out a component at every logging call site, meaning there is a "context" that already stores it and need not be re-specified.

A Component can be either empty, as when default-constructed, indicated by empty() returning true; or non-empty, in which case it actually stores something interesting. In the latter case, construct it with the templated one-arg constructor. The template arg Payload must, always, be user's own enum class. In order to have this Payload interpreted correctly, one can (and usually should) use the log::Config facility which is used by out-of-the-box Logger implementations including Simple_ostream_logger (useful for console output) and Async_file_logger (for heavy-duty file logging, including rotation support). However, this is technically optional: one can implement their own Logger which might not use the Config facility and thus deal with the meaning of Component in some completely different way. I'd start with an existing Logger implementation however; and if writing one's own Logger, then still have it use log::Config, unless that system is somehow insufficient or inappropriate.

Tip: Arguably the best way to see how to use all this together, just see flow::Flow_log_component and how Flow's own code (which, itself, logs!) uses the log system. This is discussed in more detail in the class Config doc header. Lastly, some clarifying discussion may be found (as of this writing) in the Component::type_info() doc header.

Thread safety, mutability

A Component is immutable as of this writing, except one can trivially overwrite a Component via an assignment operator. The latter write operation is not thread-safe w.r.t. a given *this, though in practice this is unlikely to ever matter.

Implementation discussion

Again – a Component conceptually must store only 2 things:

Why these 2 values?

The operations required to implement, then, are:

The first version of Component did this by simply storing (and thus being, in terms of data, equal to) boost::any. The constructor would simply load the enum value of type T as the any payload. The accessors would any_cast<T>() and then trivially access the value itself and/or its typeid().

This was delightful in terms of code simplicity. However, due to the extreme prevalence of Logger::should_log() checks in code – at every log call site, including high-verbosity ones that do not typically result in messages being logged, such as for Sev::S_TRACE – certain perf aspects of boost::any involved non-trivial perf costs. Namely:

Hence the current solution optimized those issues away by making use of the fact we know the stored thing is always an enum class. Hence:

Some discussion for perspective:

Definition at line 839 of file log.hpp.

Member Typedef Documentation

◆ enum_raw_t

using flow::log::Component::enum_raw_t = unsigned int

The type Payload must be enum class Payload : enum_raw_t: an enum type encoded via this integer type.

Definition at line 845 of file log.hpp.

Constructor & Destructor Documentation

◆ Component() [1/4]

flow::log::Component::Component ( )

Constructs a Component that stores no payload; meaning an unspecified-component Component that returns empty() == true.

Every Logger and all other systems must accept a message with such a null Component. However, one can configure a given Logger to ensure such messages not be logged (filtered out via should_log()). Point is, any log call site that supplied a null Component must still work, meaning not cause undefined behavior.

Definition at line 157 of file log.cpp.

◆ Component() [2/4]

template<typename Payload >
flow::log::Component::Component ( Payload  payload)

Constructs a Component with the given payload of arbitrary type, so long as that type is an enum class : Component::enum_raw_t.

(enum_raw_t is an unsigned integer type.) The resulting Component will return empty() == false.

Template Parameters
PayloadThe type of the component value stored inside *this. This is required to be a type satisfying the requirements in the doc header for enum_raw_t.
Parameters
payloadThe payload value copied into *this and whenever a Component itself is copied (or moved).

Definition at line 1729 of file log.hpp.

References operator=(), and payload().

Here is the call graph for this function:

◆ Component() [3/4]

flow::log::Component::Component ( const Component src)
default

Copies the source Component into *this.

This involves a single payload copy (and not even that if src.empty()).

Parameters
srcObject to copy.

◆ Component() [4/4]

flow::log::Component::Component ( Component &&  src_moved)
default

Constructs *this equal to src_moved.

In this implementation it is equivalent to the copy constructor.

Parameters
src_movedObject to move.

Member Function Documentation

◆ empty()

bool flow::log::Component::empty ( ) const

Returns true if *this is as if default-constructed (a null Component); false if as if constructed via the 1-arg constructor (a non-null Component).

Returns
See above.

Definition at line 168 of file log.cpp.

References m_payload_type_or_null.

Referenced by flow::log::Config::output_component_to_ostream(), flow::log::Config::output_whether_should_log(), payload_enum_raw_value(), and payload_type().

Here is the caller graph for this function:

◆ operator=() [1/3]

Component & flow::log::Component::operator= ( Component &&  src_moved)
default

Makes *this equal to src_moved.

In this implementation it is equivalent to copy assignment.

Parameters
src_movedObject to move.
Returns
*this.

◆ operator=() [2/3]

Component & flow::log::Component::operator= ( const Component src)
default

Overwrites *this with a copy of src.

Parameters
srcObject to copy.
Returns
*this.

Referenced by Component().

Here is the caller graph for this function:

◆ operator=() [3/3]

template<typename Payload >
Component & flow::log::Component::operator= ( Payload  new_payload)

Equivalent to operator=(Component<Payload>(new_payload)) modulo possibly minor perf differences.

Parameters
new_payloadSee non-default constructor.
Returns
*this.

Definition at line 1743 of file log.hpp.

References m_payload_enum_raw_value, and m_payload_type_or_null.

◆ payload()

template<typename Payload >
Payload flow::log::Component::payload

Returns reference to immutable payload stored in *this; undefined behavior if empty() == true.

Template Parameters
PayloadSee one-arg ctor doc header.
Returns
See above.

Definition at line 1736 of file log.hpp.

References m_payload_enum_raw_value.

Referenced by Component().

Here is the caller graph for this function:

◆ payload_enum_raw_value()

Component::enum_raw_t flow::log::Component::payload_enum_raw_value ( ) const

Returns the numeric value of the enum payload stored by this Component, originating in the one-arg constructor; undefined behavior if empty() == true.

Returns
See above. Specifically that's: static_cast<enum_raw_t>(payload), where payload was passed to originating 1-arg ctor.

Definition at line 187 of file log.cpp.

References empty(), and m_payload_enum_raw_value.

Referenced by flow::log::Config::component_to_union_idx().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ payload_type()

const std::type_info & flow::log::Component::payload_type ( ) const

Returns typeid(Payload), where Payload was the template param used when calling the originating one-arg constructor or equivalent; undefined behavior if empty() == true.

flow::log user that relies fully on an out-of-the-box Logger, or on a custom Logger that nevertheless fully uses the log::Config facility, is unlikely to need this information. (It is used internally by log::Config.) However, a custom Logger that decided to use an alternative Component mechanism (as opposed to how log::Config and reliant Loggers do) can use this payload_type() method to distinguish between potential source enums that were passed to the Logger at each given log call site.

For example, consider that Flow itself, for its own logging, uses the flow::Flow_log_component enum at all log call sites. Imagine you, the user, have decided to generate your own flow::log messages and always use your own enum class cool_project::Cool_log_component at your own logging call sites. Then, a typical component specification will look like this respectively:

{
Node(...) :
Log_context(..., Flow_log_component::S_NET_FLOW) // This class will usually use the NET_FLOW component.
...
class cool_project::Cool_class_about_widgets : public Log_context
{
Cool_class_about_widgets(...) :
Log_context(..., Cool_log_component::S_WIDGETRY) // Ditto for your own code. Use your own enum.
Convenience class that simply stores a Logger and/or Component passed into a constructor; and returns...
Definition: log.hpp:1619
An object of this class is a single Flow-protocol networking node, in the sense that: (1) it has a di...
Definition: node.hpp:937
Flow_log_component
The flow::log::Component payload enumeration comprising various log components used by Flow's own int...
Definition: common.hpp:632

Now, this->get_log_component().payload_type() will equal that of Flow_log_component and Cool_log_component, respectively, within those 2 classes. In particular, a custom Logger implementation can use payload_type() – and in particular the derived payload_type_index() – to interpret the Components of values from the 2 entirely disparate enums in different ways. More in particular, the out-of-the-box Loggers use Config to do all of that without your having to worry about it (but your own Logger would potentially have to worry about it particularly if not using log::Config... though we suggest that you should, barring excellent design counter-reasons).

Returns
See above.

Definition at line 173 of file log.cpp.

References empty(), and m_payload_type_or_null.

Referenced by payload_type_index().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ payload_type_index()

std::type_index flow::log::Component::payload_type_index ( ) const

Convenience accessor that returns std::type_index(payload_type()), which can be used most excellently to store things in associative containers (like std::map) keyed by disparate Component payload enum types.

(E.g., such a map might have one entry for Flow's own flow::Flow_log_component; one for the example cool_project::Cool_log_component enumeration mentioned in the payload_type() doc header; and so on for any logging modules in your process. Again, log::Config will do that for you, if you choose to use it in your custom Logger.)

Impl/perf discussion

I (ygoldfel) considered not storing a type_info – and hence not even providing payload_type() API – but directly storing type_index only (and hence only providing payload_type_index()). The plus would have been eliminating computation in type_index() construction, in the payload_type_index() accessor which is executed very frequently. The minus would have been the lack of type_info access, so for instance the name of the enum type could not be printed.

I almost did this; but since type_index() ctor is inlined, and all possible type_infos are actually generated at compile time before the program proper executes, the accessor likely reduces to a constant anyway in optimized code. That said, if profiler results contradict this expectation, we can perform this optimization anyway. However – that would be a breaking change once a Flow user uses payload_type() publicly.

Returns
See above.

Definition at line 179 of file log.cpp.

References payload_type().

Referenced by flow::log::Config::component_to_union_idx().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_payload_enum_raw_value

enum_raw_t flow::log::Component::m_payload_enum_raw_value
private

The internally stored integer representation of the enum value passed to the 1-arg constructor; meaningless and ignored if 0-arg ctor was used (empty() is true).

Definition at line 1030 of file log.hpp.

Referenced by operator=(), payload(), and payload_enum_raw_value().

◆ m_payload_type_or_null

std::type_info const* flow::log::Component::m_payload_type_or_null
private

The typeid() of the Payload passed to the 1-arg constructor; if 0-arg ctor was used (empty() is true) then it is null.

Rationale

Why is it a pointer and not something else? Answer: It cannot be a direct member: type_info is not copyable. It cannot be a non-const reference for the same reason. It cannot be a const reference, because Component is mutable via assignment. (In any case, all possible type_info objects are known before program start and are 1-1 with all possible types; hence the desire to store a "copy" is wrong-headed perf-wise or otherwise; there is just no reason for it.)

Definition at line 1024 of file log.hpp.

Referenced by empty(), operator=(), and payload_type().


The documentation for this class was generated from the following files: