#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);
}
}