aboutsummaryrefslogblamecommitdiffstats
path: root/library/cpp/lwtrace/signature.h
blob: 2c68aa40bfa31fc04bd6ef66b9fa1e007eb81fcd (plain) (tree)
1
2
3
4
5
6
7
8
9
            
                         
                                                  
 
                              
                                
                                    
                                

                               
                                                      
 
                                                  
                      




























































                                                                                                                                  
                                                     


                          
                                               



















































                                                          
                                                  






































                                                                        
                                                  






















                                                                                
                                                  
















                                                          
                                                  













                                                               
                                                      


















                                                               
                                                      















                                                              
                                                      












































































































                                                                                      
                                                                                        









                                                                                                                                               
                                                                                     












                                                                              








                                                                                   


































                                                                                                          






                                                                                            
                                                                                                        













                                                                                                                                                       
      





























                                                                                                                   

                                                                                                 


                                                                                                          
                                                                                                 


                                                                                                 








                                                                                                 
                                                            
                                              
                    
                              
                       
                               







                                                                                       
      














































































































                                                                                             



























                                                                                                                                                                                                         


































                                                                                             



























                                                                                                   
#pragma once

#include "preprocessor.h"

#include <library/cpp/lwtrace/protos/lwtrace.pb.h>

#include <util/generic/cast.h>
#include <util/generic/string.h>
#include <util/generic/typetraits.h>
#include <util/string/builder.h>
#include <util/string/cast.h>
#include <util/string/printf.h>

#include <google/protobuf/descriptor.h>
#include <google/protobuf/generated_enum_reflection.h>

#include <library/cpp/lwtrace/protos/lwtrace.pb.h>

#include <type_traits>

namespace NLWTrace {
    // Class to hold parameter values parsed from trace query predicate operators
    template <class T>
    struct TParamConv {
        static T FromString(const TString& text) {
            return ::FromString<T>(text);
        }
        static TString ToString(const T& param) {
            return ::ToString(param);
        }
    };

    template <>
    struct TParamConv<TString const*> {
        static TString FromString(const TString& text) {
            return text;
        }
        static TString ToString(TString const* param) {
            return TString(*param);
        }
    };

    template <>
    struct TParamConv<ui8> {
        static ui8 FromString(const TString& text) {
            return (ui8)::FromString<ui16>(text);
        }
        static TString ToString(ui8 param) {
            return ::ToString((ui16)param);
        }
    };

    template <>
    struct TParamConv<i8> {
        static i8 FromString(const TString& text) {
            return (i8)::FromString<i16>(text);
        }
        static TString ToString(i8 param) {
            return ::ToString((i16)param);
        }
    };

    template <>
    struct TParamConv<double> {
        static double FromString(const TString& text) {
            return ::FromString<double>(text);
        }
        static TString ToString(double param) {
            return Sprintf("%.6lf", param);
        }
    };

    // Fake parameter type used as a placeholder for not used parameters (above the number of defined params for a specific probe)
    class TNil {
    };

    // Struct that holds and handles a value of parameter of any supported type.
    struct TParam {
        char Data[LWTRACE_MAX_PARAM_SIZE];

        template <class T>
        const T& Get() const {
            return *reinterpret_cast<const T*>(Data);
        }

        template <class T>
        T& Get() {
            return *reinterpret_cast<T*>(Data);
        }

        template <class T>
        void DefaultConstruct() {
            new (&Data) T();
        }

        template <class T>
        void CopyConstruct(const T& other) {
            new (&Data) T(other);
        }

        template <class T>
        void Destruct() {
            Get<T>().~T();
        }
    };

    template <>
    inline void TParam::DefaultConstruct<TNil>() {
    }

    template <>
    inline void TParam::CopyConstruct<TNil>(const TNil&) {
    }

    template <>
    inline void TParam::Destruct<TNil>() {
    }

    class TTypedParam {
    private:
        EParamTypePb Type;
        TParam Param; // Contains garbage if PT_UNKNOWN
    public:
        TTypedParam()
            : Type(PT_UNKNOWN)
        {
        }

        explicit TTypedParam(EParamTypePb type)
            : Type(type)
        {
            switch (Type) {
#define FOREACH_PARAMTYPE_MACRO(n, t, v) \
    case PT_##v:                         \
        Param.DefaultConstruct<t>();     \
        return;
                FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO
                case PT_UNKNOWN:
                    return;
                default:
                    Y_ABORT("unknown param type");
            }
        }

        template <class T>
        explicit TTypedParam(const T& x, EParamTypePb type = PT_UNKNOWN)
            : Type(type)
        {
            Param.CopyConstruct<T>(x);
        }

#define FOREACH_PARAMTYPE_MACRO(n, t, v) \
    explicit TTypedParam(const t& x)     \
        : Type(PT_##v) {                 \
        Param.CopyConstruct<t>(x);       \
    }
        FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO

        TTypedParam(const TTypedParam& o) {
            Assign(o);
        }

        TTypedParam& operator=(const TTypedParam& o) {
            Reset();
            Assign(o);
            return *this;
        }

        void Assign(const TTypedParam& o) {
            Type = o.Type;
            switch (Type) {
#define FOREACH_PARAMTYPE_MACRO(n, t, v)          \
    case PT_##v:                                  \
        Param.CopyConstruct<t>(o.Param.Get<t>()); \
        return;
                FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO
                case PT_UNKNOWN:
                    return;
                default:
                    Y_ABORT("unknown param type");
            }
        }

        TTypedParam(TTypedParam&& o)
            : Type(o.Type)
            , Param(o.Param)
        {
            o.Type = PT_UNKNOWN; // To avoid Param destroy by source object dtor
        }

        TTypedParam(EParamTypePb type, const TParam& param)
            : Type(type)
        {
            Y_UNUSED(param); // for disabled lwtrace
            switch (Type) {
#define FOREACH_PARAMTYPE_MACRO(n, t, v)        \
    case PT_##v:                                \
        Param.CopyConstruct<t>(param.Get<t>()); \
        return;
                FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO
                case PT_UNKNOWN:
                    return;
                default:
                    Y_ABORT("unknown param type");
            }
        }

        ~TTypedParam() {
            Reset();
        }

        void Reset() {
            switch (Type) {
#define FOREACH_PARAMTYPE_MACRO(n, t, v) \
    case PT_##v:                         \
        Param.Destruct<t>();             \
        return;
                FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO
                case PT_UNKNOWN:
                    return;
                default:
                    Y_ABORT("unknown param type");
            }
            Type = PT_UNKNOWN;
        }

        bool operator==(const TTypedParam& rhs) const {
            if (Y_LIKELY(Type == rhs.Type)) {
                switch (Type) {
#define FOREACH_PARAMTYPE_MACRO(n, t, v) \
    case PT_##v:                         \
        return Param.Get<t>() == rhs.Param.Get<t>();
                    FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO
                    case PT_UNKNOWN:
                        return false; // All unknowns are equal
                    default:
                        Y_ABORT("unknown param type");
                }
            } else {
                return false;
            }
        }
        bool operator!=(const TTypedParam& rhs) const {
            return !operator==(rhs);
        }

        bool operator<(const TTypedParam& rhs) const {
            if (Y_LIKELY(Type == rhs.Type)) {
                switch (Type) {
#define FOREACH_PARAMTYPE_MACRO(n, t, v) \
    case PT_##v:                         \
        return Param.Get<t>() < rhs.Param.Get<t>();
                    FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO
                    case PT_UNKNOWN:
                        return false; // All unknowns are equal
                    default:
                        Y_ABORT("unknown param type");
                }
            } else {
                return Type < rhs.Type;
            }
        }

        bool operator<=(const TTypedParam& rhs) const {
            if (Y_LIKELY(Type == rhs.Type)) {
                switch (Type) {
#define FOREACH_PARAMTYPE_MACRO(n, t, v) \
    case PT_##v:                         \
        return Param.Get<t>() <= rhs.Param.Get<t>();
                    FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO
                    case PT_UNKNOWN:
                        return true; // All unknowns are equal
                    default:
                        Y_ABORT("unknown param type");
                }
            } else {
                return Type < rhs.Type;
            }
        }

        bool operator>(const TTypedParam& rhs) const {
            return !operator<=(rhs);
        }
        bool operator>=(const TTypedParam& rhs) const {
            return !operator<(rhs);
        }

        EParamTypePb GetType() const {
            return Type;
        }
        const TParam& GetParam() const {
            return Param;
        }
    };

    class TLiteral {
    private:
        TTypedParam Values[EParamTypePb_ARRAYSIZE];

    public:
        explicit TLiteral(const TString& text) {
            Y_UNUSED(text); /* That's for windows, where we have lwtrace disabled. */

#define FOREACH_PARAMTYPE_MACRO(n, t, v)                               \
    try {                                                              \
        Values[PT_##v] = TTypedParam(TParamConv<t>::FromString(text)); \
    } catch (...) {                                                    \
        Values[PT_##v] = TTypedParam();                                \
    }                                                                  \
    /**/
            FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO
        }

        TLiteral() {
        }

        TLiteral(const TLiteral& o) {
            for (size_t i = 0; i < EParamTypePb_ARRAYSIZE; i++) {
                Values[i] = o.Values[i];
            }
        }

        TLiteral& operator=(const TLiteral& o) {
            for (size_t i = 0; i < EParamTypePb_ARRAYSIZE; i++) {
                Values[i] = o.Values[i];
            }
            return *this;
        }

        const TTypedParam& GetValue(EParamTypePb type) const {
            return Values[type];
        }

        bool operator==(const TTypedParam& rhs) const {
            return Values[rhs.GetType()] == rhs;
        }
        bool operator!=(const TTypedParam& rhs) const {
            return !operator==(rhs);
        }

        bool operator<(const TTypedParam& rhs) const {
            return Values[rhs.GetType()] < rhs;
        }

        bool operator<=(const TTypedParam& rhs) const {
            return Values[rhs.GetType()] <= rhs;
        }

        bool operator>(const TTypedParam& rhs) const {
            return !operator<=(rhs);
        }
        bool operator>=(const TTypedParam& rhs) const {
            return !operator<(rhs);
        }
    };

    inline bool operator==(const TTypedParam& lhs, const TLiteral& rhs) {
        return lhs == rhs.GetValue(lhs.GetType());
    }
    inline bool operator!=(const TTypedParam& lhs, const TLiteral& rhs) {
        return !operator==(lhs, rhs);
    }

    inline bool operator<(const TTypedParam& lhs, const TLiteral& rhs) {
        return lhs < rhs.GetValue(lhs.GetType());
    }

    inline bool operator<=(const TTypedParam& lhs, const TLiteral& rhs) {
        return lhs <= rhs.GetValue(lhs.GetType());
    }

    inline bool operator>(const TTypedParam& lhs, const TLiteral& rhs) {
        return !operator<=(lhs, rhs);
    }
    inline bool operator>=(const TTypedParam& lhs, const TLiteral& rhs) {
        return !operator<(lhs, rhs);
    }

    // Struct that holds and handles all parameter values of different supported types
    struct TParams {
        TParam Param[LWTRACE_MAX_PARAMS];
    };

    using TSerializedParams = google::protobuf::RepeatedPtrField<NLWTrace::TTraceParam>;

    // Represents a common class for all function "signatures" (parameter types and names).
    // Provides non-virtual interface to handle the signature and (emulated) virtual interface to handle TParams corresponding to the signature
    struct TSignature {
        const char** ParamTypes;
        const char* ParamNames[LWTRACE_MAX_PARAMS + 1];
        size_t ParamCount;

        // Virtual table
        void (*SerializeParamsFunc)(const TParams& params, TString* values);
        void (*CloneParamsFunc)(TParams& newParams, const TParams& oldParams);
        void (*DestroyParamsFunc)(TParams& params);
        void (*SerializeToPbFunc)(const TParams& params, TSerializedParams& arr);
        bool (*DeserializeFromPbFunc)(TParams& params, const TSerializedParams& arr);

        // Virtual calls emulation
        void SerializeParams(const TParams& params, TString* values) const {
            (*SerializeParamsFunc)(params, values);
        }

        void CloneParams(TParams& newParams, const TParams& oldParams) const {
            (*CloneParamsFunc)(newParams, oldParams);
        }

        void DestroyParams(TParams& params) const {
            (*DestroyParamsFunc)(params);
        }

        void SerializeToPb(const TParams& params, TSerializedParams& arr) const
        {
            (*SerializeToPbFunc)(params, arr);
        }

        bool DeserializeFromPb(TParams& params, const TSerializedParams& arr) const
        {
            return (*DeserializeFromPbFunc)(params, arr);
        }

        void ToProtobuf(TEventPb& pb) const;

        size_t FindParamIndex(const TString& param) const {
            for (size_t i = 0; i < ParamCount; i++) {
                if (ParamNames[i] == param) {
                    return i;
                }
            }
            return size_t(-1);
        }
    };

#ifndef LWTRACE_DISABLE

    // Implementation. Used for compilation error if not all expected parameters passed to a function call
    struct ERROR_not_enough_parameters : TNil {};

    // Struct that holds static string with a name of parameter type
    template <class T>
    struct TParamType {
        enum { Supported = 0 };
        static const char* NameString;
    };

#define FOREACH_PARAMTYPE_MACRO(n, t, v) \
    template <>                          \
    struct TParamType<t> {               \
        enum { Supported = 1 };          \
        static const char* NameString;   \
    };                                   \
    /**/
    FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
    FOR_NIL_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO

    template <class T>
    struct TParamTraits;

    // Enum types traits impl.
    template <class TEnum, class = std::enable_if_t<std::is_enum_v<TEnum>>>
    struct TEnumParamTraitsImpl {
        using TStoreType = typename TParamTraits<std::underlying_type_t<TEnum>>::TStoreType;
        using TFuncParam = TEnum;

        inline static void ToString(typename TTypeTraits<TStoreType>::TFuncParam stored, TString* out) {
            if constexpr (google::protobuf::is_proto_enum<TEnum>::value) {
                const google::protobuf::EnumValueDescriptor* valueDescriptor = google::protobuf::GetEnumDescriptor<TEnum>()->FindValueByNumber(stored);
                if (valueDescriptor) {
                    *out = TStringBuilder() << valueDescriptor->name() << " (" << stored << ")";
                } else {
                    *out = TParamConv<TStoreType>::ToString(stored);
                }
            } else {
                *out = TParamConv<TStoreType>::ToString(stored);
            }
        }

        inline static TStoreType ToStoreType(TFuncParam v) {
            return static_cast<TStoreType>(v);
        }
    };

    template <class TCustomType>
    struct TCustomTraitsImpl {
        using TStoreType = typename TParamTraits<typename TCustomType::TStoreType>::TStoreType; //see STORE_TYPE_AS
        using TFuncParam = typename TCustomType::TFuncParam;

        inline static void ToString(typename TTypeTraits<TStoreType>::TFuncParam stored, TString* out) {
            TCustomType::ToString(stored, out);
        }

        inline static TStoreType ToStoreType(TFuncParam v) {
            return TCustomType::ToStoreType(v);
        }
    };

    template <class T, bool isEnum>
    struct TParamTraitsImpl;

    template <class TEnum>
    struct TParamTraitsImpl<TEnum, true> : TEnumParamTraitsImpl<TEnum> {
    };

    template <class TCustomType>
    struct TParamTraitsImpl<TCustomType, false> : TCustomTraitsImpl<TCustomType> {
    };

    template <class T>
    struct TParamTraits : TParamTraitsImpl<T, std::is_enum_v<T>> {
    };

    // Standard stored types traits.

#define STORE_TYPE_AS(input_t, store_as_t)                                                      \
    template <>                                                                                 \
    struct TParamTraits<input_t> {                                                              \
        using TStoreType = store_as_t;                                                          \
        using TFuncParam = typename TTypeTraits<input_t>::TFuncParam;                           \
                                                                                                \
        inline static void ToString(typename TTypeTraits<TStoreType>::TFuncParam stored, TString* out) { \
            *out = TParamConv<TStoreType>::ToString(stored);                                    \
        }                                                                                       \
                                                                                                \
        inline static TStoreType ToStoreType(TFuncParam v) {                                    \
            return v;                                                                           \
        }                                                                                       \
    };                                                                                          \
    /**/
    STORE_TYPE_AS(ui8, ui64);
    STORE_TYPE_AS(i8, i64);
    STORE_TYPE_AS(ui16, ui64);
    STORE_TYPE_AS(i16, i64);
    STORE_TYPE_AS(ui32, ui64);
    STORE_TYPE_AS(i32, i64);
    STORE_TYPE_AS(bool, ui64);
    STORE_TYPE_AS(float, double);
#define FOREACH_PARAMTYPE_MACRO(n, t, v) STORE_TYPE_AS(t, t)
    FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef STORE_TYPE_AS
#undef FOREACH_PARAMTYPE_MACRO

    // Nil type staits.
    template <>
    struct TParamTraits<TNil> {
        using TStoreType = TNil;
        using TFuncParam = TTypeTraits<TNil>::TFuncParam;

        inline static void ToString(typename TTypeTraits<TNil>::TFuncParam, TString*) {
        }

        inline static TNil ToStoreType(TFuncParam v) {
            return v;
        }
    };

    inline EParamTypePb ParamTypeToProtobuf(const char* paramType) {
#define FOREACH_PARAMTYPE_MACRO(n, t, v) \
    if (strcmp(paramType, n) == 0) {     \
        return PT_##v;                   \
    }                                    \
    /**/
        FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO)
#undef FOREACH_PARAMTYPE_MACRO
        return PT_UNKNOWN;
    }

    template <typename T>
    inline void SaveParamToPb(TSerializedParams& msg, const TParam& param);

    template <>
    inline void SaveParamToPb<TNil>(TSerializedParams& msg, const TParam& param)
    {
        Y_UNUSED(msg);
        Y_UNUSED(param);
    }

    template <>
    inline void SaveParamToPb<i64>(TSerializedParams& msg, const TParam& param)
    {
        msg.Add()->SetIntValue(param.Get<typename TParamTraits<i64>::TStoreType>());
    }

    template <>
    inline void SaveParamToPb<ui64>(TSerializedParams& msg, const TParam& param)
    {
        msg.Add()->SetUintValue(param.Get<typename TParamTraits<ui64>::TStoreType>());
    }

    template <>
    inline void SaveParamToPb<double>(TSerializedParams& msg, const TParam& param)
    {
        msg.Add()->SetDoubleValue(param.Get<typename TParamTraits<double>::TStoreType>());
    }

    template <>
    inline void SaveParamToPb<TString>(TSerializedParams& msg, const TParam& param)
    {
        msg.Add()->SetStrValue(param.Get<typename TParamTraits<TString>::TStoreType>());
    }

    template <>
    inline void SaveParamToPb<TSymbol>(TSerializedParams& msg, const TParam& param)
    {
        msg.Add()->SetStrValue(*param.Get<typename TParamTraits<TSymbol>::TStoreType>().Str);
    }

    template <>
    inline void SaveParamToPb<TCheck>(TSerializedParams& msg, const TParam& param)
    {
        msg.Add()->SetIntValue(param.Get<typename TParamTraits<TCheck>::TStoreType>().Value);
    }

    template <typename T>
    inline void LoadParamFromPb(const TTraceParam& msg, TParam& param);

    template <>
    inline void LoadParamFromPb<i64>(const TTraceParam& msg, TParam& param)
    {
        param.DefaultConstruct<i64>();
        param.Get<i64>() = msg.GetIntValue();
    }

    template <>
    inline void LoadParamFromPb<ui64>(const TTraceParam& msg, TParam& param)
    {
        param.DefaultConstruct<ui64>();
        param.Get<ui64>() = msg.GetUintValue();
    }

    template <>
    inline void LoadParamFromPb<double>(const TTraceParam& msg, TParam& param)
    {
        param.DefaultConstruct<double>();
        param.Get<double>() = msg.GetDoubleValue();
    }

    template <>
    inline void LoadParamFromPb<TCheck>(const TTraceParam& msg, TParam& param)
    {
        param.CopyConstruct<TCheck>(TCheck(msg.GetIntValue()));
    }

    template <>
    inline void LoadParamFromPb<TSymbol>(const TTraceParam& msg, TParam& param)
    {
        Y_UNUSED(msg);
        Y_UNUSED(param);
        static TString unsupported("unsupported");
        // so far TSymbol deserialization is not supported
        // since it is not used for probes, it is ok
        param.CopyConstruct<TSymbol>(TSymbol(&unsupported));
    }

    template <>
    inline void LoadParamFromPb<TString>(const TTraceParam& msg, TParam& param)
    {
        param.DefaultConstruct<TString>();
        param.Get<TString>() = msg.GetStrValue();
    }

    template <>
    inline void LoadParamFromPb<TNil>(const TTraceParam& msg, TParam& param)
    {
        Y_UNUSED(msg);
        Y_UNUSED(param);
    }

    // Class representing a specific signature
    template <LWTRACE_TEMPLATE_PARAMS>
    struct TUserSignature {
#define FOREACH_PARAMNUM_MACRO(i) static_assert(TParamType<typename TParamTraits<TP##i>::TStoreType>::Supported == 1, "expect TParamType< typename TParamTraits<TP ## i>::TStoreType >::Supported == 1");
        FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO) // ERROR: unsupported type used as probe/event parameter type
#undef FOREACH_PARAMNUM_MACRO
        static const char* ParamTypes[];
        static const int ParamCount = LWTRACE_COUNT_PARAMS;

        // Implementation of virtual function (TSignature derived classes vtable emulation)
        inline static void SerializeParams(const TParams& params, TString* values) {
#define FOREACH_PARAMNUM_MACRO(i) TParamTraits<TP##i>::ToString(params.Param[i].Get<typename TParamTraits<TP##i>::TStoreType>(), values + i);
            FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO);
#undef FOREACH_PARAMNUM_MACRO
        }

        // Implementation of virtual function (TSignature derived classes vtable emulation)
        inline static void CloneParams(TParams& newParams, const TParams& oldParams) {
#define FOREACH_PARAMNUM_MACRO(i) newParams.Param[i].CopyConstruct<typename TParamTraits<TP##i>::TStoreType>(oldParams.Param[i].Get<typename TParamTraits<TP##i>::TStoreType>());
            FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO);
#undef FOREACH_PARAMNUM_MACRO
        }

        // Implementation of virtual function (TSignature derived classes vtable emulation)
        inline static void DestroyParams(TParams& params) {
#define FOREACH_PARAMNUM_MACRO(i) params.Param[i].Destruct<typename TParamTraits<TP##i>::TStoreType>();
            FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO);
#undef FOREACH_PARAMNUM_MACRO
        }

        // Implementation of virtual function (TSignature derived classes vtable emulation)
        inline static void SerializeToPb(const TParams& params, TSerializedParams& arr)
        {
#define FOREACH_PARAMNUM_MACRO(i)                                              \
    SaveParamToPb<typename TParamTraits<TP##i>::TStoreType>(                   \
        arr,                                                                   \
        params.Param[i]);                                                      \
// FOREACH_PARAMNUM_MACRO
            FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO);
#undef FOREACH_PARAMNUM_MACRO
        }

        // Implementation of virtual function (TSignature derived classes vtable emulation)
        inline static bool DeserializeFromPb(TParams& params, const TSerializedParams& arr) {
            if (arr.size() != ParamCount) {
                return false;
            }
            if (!ParamCount) {
                return true;
            }

            int paramIdx = 0;
#define FOREACH_PARAMNUM_MACRO(i)                                              \
    if (paramIdx >= arr.size()) {                                              \
        return true;                                                           \
    };                                                                         \
    LoadParamFromPb<typename TParamTraits<TP##i>::TStoreType>(                 \
        arr.Get(paramIdx),                                                     \
        params.Param[paramIdx]);                                               \
    ++paramIdx;                                                                \
//  FOREACH_PARAMNUM_MACRO
            FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO);
#undef FOREACH_PARAMNUM_MACRO
            return true;
        }
    };

    // Array of static strings pointers for names of parameter types in a specific signature
    template <LWTRACE_TEMPLATE_PARAMS_NODEF>
    const char* TUserSignature<LWTRACE_TEMPLATE_ARGS>::ParamTypes[] = {
#define FOREACH_PARAMNUM_MACRO(i) TParamType<typename TParamTraits<TP##i>::TStoreType>::NameString,
        FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO) nullptr
#undef FOREACH_PARAMNUM_MACRO
    };

    inline void TSignature::ToProtobuf(TEventPb& pb) const {
        for (size_t i = 0; i < ParamCount; i++) {
            pb.AddParamTypes(ParamTypeToProtobuf(ParamTypes[i]));
            pb.AddParamNames(ParamNames[i]);
        }
    }

#else

    inline void TSignature::ToProtobuf(TEventPb&) const {
    }

    inline EParamTypePb ParamTypeToProtobuf(const char*) {
        return PT_UNKNOWN;
    }

#endif

}