#pragma once #include "traits.h" #include <google/protobuf/descriptor.h> #include <google/protobuf/message.h> #include <util/generic/cast.h> namespace NProtoBuf { // C++ compatible conversions of FieldDescriptor::CppType's using ECppType = FieldDescriptor::CppType; namespace NCast { template <ECppType src, ECppType dst> struct TIsCompatibleCppType { enum { Result = src == dst || (TIsNumericCppType<src>::Result && TIsNumericCppType<dst>::Result) }; }; template <ECppType src, ECppType dst> struct TIsEnumToNumericCppType { enum { Result = (src == FieldDescriptor::CPPTYPE_ENUM && TIsNumericCppType<dst>::Result) }; }; template <ECppType src, ECppType dst, bool compatible> // compatible == true struct TCompatCastBase { static const bool IsCompatible = true; typedef typename TCppTypeTraits<src>::T TSrc; typedef typename TCppTypeTraits<dst>::T TDst; static inline TDst Cast(TSrc value) { return value; } }; template <ECppType src, ECppType dst> // compatible == false struct TCompatCastBase<src, dst, false> { static const bool IsCompatible = false; typedef typename TCppTypeTraits<src>::T TSrc; typedef typename TCppTypeTraits<dst>::T TDst; static inline TDst Cast(TSrc) { ythrow TBadCastException() << "Incompatible FieldDescriptor::CppType conversion: #" << (size_t)src << " to #" << (size_t)dst; } }; template <ECppType src, ECppType dst, bool isEnumToNum> // enum -> numeric struct TCompatCastImpl { static const bool IsCompatible = true; typedef typename TCppTypeTraits<dst>::T TDst; static inline TDst Cast(const EnumValueDescriptor* value) { Y_ASSERT(value != nullptr); return value->number(); } }; template <ECppType src, ECppType dst> struct TCompatCastImpl<src, dst, false>: public TCompatCastBase<src, dst, TIsCompatibleCppType<src, dst>::Result> { using TCompatCastBase<src, dst, TIsCompatibleCppType<src, dst>::Result>::IsCompatible; }; template <ECppType src, ECppType dst> struct TCompatCast: public TCompatCastImpl<src, dst, TIsEnumToNumericCppType<src, dst>::Result> { typedef TCompatCastImpl<src, dst, TIsEnumToNumericCppType<src, dst>::Result> TBase; typedef typename TCppTypeTraits<src>::T TSrc; typedef typename TCppTypeTraits<dst>::T TDst; using TBase::Cast; using TBase::IsCompatible; inline bool Try(TSrc value, TDst& res) { if (IsCompatible) { res = Cast(value); return true; } return false; } }; } template <ECppType src, ECppType dst> inline typename TCppTypeTraits<dst>::T CompatCast(typename TCppTypeTraits<src>::T value) { return NCast::TCompatCast<src, dst>::Cast(value); } template <ECppType src, ECppType dst> inline bool TryCompatCast(typename TCppTypeTraits<src>::T value, typename TCppTypeTraits<dst>::T& res) { return NCast::TCompatCast<src, dst>::Try(value, res); } // Message static/dynamic checked casts template <typename TpMessage> inline const TpMessage* TryCast(const Message* msg) { if (!msg || TpMessage::descriptor() != msg->GetDescriptor()) return NULL; return CheckedCast<const TpMessage*>(msg); } template <typename TpMessage> inline const TpMessage* TryCast(const Message* msg, const TpMessage*& ret) { ret = TryCast<TpMessage>(msg); return ret; } template <typename TpMessage> inline TpMessage* TryCast(Message* msg) { if (!msg || TpMessage::descriptor() != msg->GetDescriptor()) return nullptr; return CheckedCast<TpMessage*>(msg); } template <typename TpMessage> inline TpMessage* TryCast(Message* msg, TpMessage*& ret) { ret = TryCast<TpMessage>(msg); return ret; } // specialize for Message itself template <> inline const Message* TryCast<Message>(const Message* msg) { return msg; } template <> inline Message* TryCast<Message>(Message* msg) { return msg; } // Binary serialization compatible conversion inline bool TryBinaryCast(const Message* from, Message* to, TString* buffer = nullptr) { TString tmpbuf; if (!buffer) buffer = &tmpbuf; if (!from->SerializeToString(buffer)) return false; return to->ParseFromString(*buffer); } }