Program Listing for File channel.hpp
↰ Return to documentation for file (include/data_tamer/channel.hpp
)
#pragma once
#include "data_tamer/values.hpp"
#include "data_tamer/data_sink.hpp"
#include "data_tamer/details/mutex.hpp"
#include "data_tamer/logged_value.hpp"
#include <chrono>
#include <memory>
namespace DataTamer
{
// Utility
inline std::chrono::nanoseconds NsecSinceEpoch()
{
auto since_epoch = std::chrono::system_clock::now().time_since_epoch();
return std::chrono::duration_cast<std::chrono::nanoseconds>(since_epoch);
}
class DataSinkBase;
class LogChannel;
class ChannelsRegistry;
//---------------------------------------------------------
class LogChannel : public std::enable_shared_from_this<LogChannel>
{
protected:
// We make this private because the object must be wrapped
// inside a std::shared_ptr.
// This allows us to use std::weak_ptr in LoggedValue
LogChannel(std::string name);
public:
static std::shared_ptr<LogChannel> create(std::string name);
~LogChannel();
LogChannel(const LogChannel&) = delete;
LogChannel& operator=(const LogChannel&) = delete;
LogChannel(LogChannel&&) = delete;
LogChannel& operator=(LogChannel&&) = delete;
template <typename T>
RegistrationID registerValue(const std::string& name, const T* value);
template <template <class, class> class Container, class T, class... TArgs>
RegistrationID registerValue(const std::string& name,
const Container<T, TArgs...>* value);
template <typename T, size_t N>
RegistrationID registerValue(const std::string& name, const std::array<T, N>* value);
template <typename T>
RegistrationID registerCustomValue(const std::string& name, const T* value,
CustomSerializer::Ptr type_info);
template <typename T = double>
[[nodiscard]] std::shared_ptr<LoggedValue<T>> createLoggedValue(std::string const& name,
T initial_value = T{});
[[nodiscard]] const std::string& channelName() const;
void setEnabled(const RegistrationID& id, bool enable);
void unregister(const RegistrationID& id);
void addDataSink(std::shared_ptr<DataSinkBase> sink);
bool takeSnapshot(std::chrono::nanoseconds timestamp = NsecSinceEpoch());
[[nodiscard]] const ActiveMask& getActiveFlags();
[[nodiscard]] Schema getSchema() const;
Mutex& writeMutex();
private:
struct Pimpl;
std::unique_ptr<Pimpl> _p;
TypesRegistry _type_registry;
template <typename T>
void updateTypeRegistry();
template <typename T>
void updateTypeRegistryImpl(FieldsVector& fields, const char* name);
void addCustomType(const std::string& custom_type_name, const FieldsVector& fields);
[[nodiscard]] RegistrationID registerValueImpl(const std::string& name,
ValuePtr&& value_ptr,
CustomSerializer::Ptr type_info);
};
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//----------------------------------------------------------------------
template <typename T>
void LogChannel::updateTypeRegistryImpl(FieldsVector& fields, const char* field_name)
{
using SerializeMe::container_info;
TypeField field;
field.field_name = field_name;
field.type = GetBasicType<T>();
if constexpr(GetBasicType<T>() == BasicType::OTHER)
{
field.type_name = CustomTypeName<T>::get();
if constexpr(container_info<T>::is_container)
{
field.is_vector = true;
field.array_size = container_info<T>::size;
using Type = typename container_info<T>::value_type;
updateTypeRegistry<Type>();
}
else
{
updateTypeRegistry<T>();
}
}
fields.push_back(field);
}
template <typename T>
inline void LogChannel::updateTypeRegistry()
{
if constexpr(IsNumericType<T>())
{
return;
}
using namespace SerializeMe;
static_assert(has_TypeDefinition<T>(), "Missing TypeDefinition");
FieldsVector fields;
const std::string type_name(CustomTypeName<T>::get());
if(auto added_serializer = _type_registry.addType<T>(type_name, true))
{
auto func = [this, &fields](const char* field_name, const auto* member) {
using MemberType =
typename std::remove_cv_t<std::remove_reference_t<decltype(*member)>>;
updateTypeRegistryImpl<MemberType>(fields, field_name);
};
T dummy;
TypeDefinition(dummy, func);
addCustomType(type_name, fields);
}
}
template <typename T>
inline RegistrationID LogChannel::registerValue(const std::string& name,
const T* value_ptr)
{
using namespace SerializeMe;
static_assert(has_TypeDefinition<T>() || IsNumericType<T>(), "Missing TypeDefinition");
if constexpr(IsNumericType<T>())
{
return registerValueImpl(name, ValuePtr(value_ptr), {});
}
else
{
updateTypeRegistry<T>();
auto def = _type_registry.getSerializer<T>();
return registerValueImpl(name, ValuePtr(value_ptr, def), def);
}
}
template <typename T>
inline RegistrationID LogChannel::registerCustomValue(const std::string& name,
const T* value_ptr,
CustomSerializer::Ptr serializer)
{
static_assert(!IsNumericType<T>(), "This method should be used only for custom types");
return registerValueImpl(name, ValuePtr(value_ptr, serializer), serializer);
}
template <template <class, class> class Container, class T, class... TArgs>
inline RegistrationID LogChannel::registerValue(const std::string& prefix,
const Container<T, TArgs...>* vect)
{
if constexpr(IsNumericType<T>())
{
return registerValueImpl(prefix, ValuePtr(vect), {});
}
else
{
updateTypeRegistry<T>();
auto def = _type_registry.getSerializer<T>();
return registerValueImpl(prefix, ValuePtr(vect), def);
}
}
template <typename T, size_t N>
inline RegistrationID LogChannel::registerValue(const std::string& prefix,
const std::array<T, N>* vect)
{
if constexpr(IsNumericType<T>())
{
return registerValueImpl(prefix, ValuePtr(vect), {});
}
else
{
updateTypeRegistry<T>();
auto def = _type_registry.getSerializer<T>();
return registerValueImpl(prefix, ValuePtr(vect, def), def);
}
}
template <typename T>
inline std::shared_ptr<LoggedValue<T>>
LogChannel::createLoggedValue(std::string const& name, T initial_value)
{
auto val = new LoggedValue<T>(shared_from_this(), name, initial_value);
return std::shared_ptr<LoggedValue<T>>(val);
}
template <typename T>
inline LoggedValue<T>::LoggedValue(const std::shared_ptr<LogChannel>& channel,
const std::string& name, T initial_value)
: channel_(channel), value_(initial_value), id_(channel->registerValue(name, &value_))
{}
template <typename T>
inline LoggedValue<T>::~LoggedValue()
{
if(auto channel = channel_.lock())
{
channel->unregister(id_);
}
}
template <typename T>
inline void LoggedValue<T>::setEnabled(bool enabled)
{
// lock the shared mutex in "write" mode
std::lock_guard lk(rw_mutex_);
if(auto channel = channel_.lock())
{
channel->setEnabled(id_, enabled);
}
enabled_ = enabled;
}
template <typename T>
inline void LoggedValue<T>::set(const T& val, bool auto_enable)
{
// lock the shared mutex in "write" mode
std::lock_guard lk(rw_mutex_);
if(auto channel = channel_.lock())
{
value_ = val;
if(!enabled_ && auto_enable)
{
channel->setEnabled(id_, true);
enabled_ = true;
}
}
else
{
value_ = val;
enabled_ |= auto_enable;
}
}
template <typename T>
inline T LoggedValue<T>::get()
{
// lock the shared mutex in "read" mode
rw_mutex_.lock_shared();
T tmp = value_;
rw_mutex_.unlock_shared();
return tmp;
}
template <typename T>
inline LockedPtr<T> LoggedValue<T>::getLockedPtr()
{
if(auto channel = channel_.lock())
{
return LockedPtr<T>(&value_, &channel->writeMutex());
}
return LockedPtr<T>(&value_, nullptr);
}
} // namespace DataTamer