Files
o3de/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Data/PropertyTraits.h
T
Steve Pham 38261d0800 Shorten copyright headers by splitting into 2 lines (#2213)
* Updated all copyright headers to split the longer original copyright line into 2 shorter lines

Signed-off-by: Steve Pham <spham@amazon.com>
2021-07-16 15:25:48 -07:00

570 lines
31 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/typetraits/remove_cv.h>
#include <AzCore/std/string/string_view.h>
#include <ScriptCanvas/Core/Datum.h>
#include <AzCore/std/function/invoke.h>
namespace ScriptCanvas
{
namespace Data
{
using GetterFunction = AZStd::function<AZ::Outcome<Datum, AZStd::string>(const Datum&)>;
struct GetterWrapper
{
AZ_CLASS_ALLOCATOR(GetterWrapper, AZ::SystemAllocator, 0);
GetterFunction m_getterFunction;
Data::Type m_propertyType;
AZStd::string m_propertyName;
};
using GetterContainer = AZStd::unordered_map<AZStd::string, GetterWrapper>;
using SetterFunction = AZStd::function<AZ::Outcome<void, AZStd::string>(Datum&, const Datum&)>;
struct SetterWrapper
{
AZ_CLASS_ALLOCATOR(SetterWrapper, AZ::SystemAllocator, 0);
SetterFunction m_setterFunction;
Data::Type m_propertyType;
AZStd::string m_propertyName;
};
using SetterContainer = AZStd::unordered_map<AZStd::string, SetterWrapper>;
struct PropertyMetadata
{
AZ_TYPE_INFO(PropertyMetadata, "{A4910EF1-0139-4A7A-878C-E60E18F3993A}");
AZ_CLASS_ALLOCATOR(PropertyMetadata, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
SlotId m_propertySlotId;
Data::Type m_propertyType;
AZStd::string m_propertyName;
Data::GetterFunction m_getterFunction;
};
AZStd::vector<AZ::BehaviorProperty*> GetBehaviorProperties(const Data::Type& type);
GetterContainer ExplodeToGetters(const Data::Type& type);
SetterContainer ExplodeToSetters(const Data::Type& type);
AZ_INLINE AZStd::vector<AZ::BehaviorProperty*> GetBehaviorProperties(const Data::Type& type)
{
AZ_Error("Script Canvas", type.GetType() == Data::eType::BehaviorContextObject, "GetBehaviorProperties cannot return properties for non-BehaviorContextObjexts");
AZStd::vector<AZ::BehaviorProperty*> propertiesByType;
const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(type.GetAZType());
if (behaviorClass)
{
for (auto&& namePropertyPair : behaviorClass->m_properties)
{
propertiesByType.push_back(namePropertyPair.second);
}
}
return propertiesByType;
}
namespace Internal
{
template<typename FunctionType, typename GetterType>
struct WrapGetterHelper
{
static_assert(!AZStd::is_void<GetterType>::value, "Getter function must be either a member pointer that accepts 0 arguments or an invokable object that accepts 1 argument");
using PropertyType = AZStd::decay_t<AZStd::invoke_result_t<FunctionType, GetterType>>;
static_assert(!AZStd::is_void<PropertyType>::value, "Getter function must return a non-void type");
static GetterWrapper Callback(AZStd::string_view propertyName, const FunctionType& propertyGetter)
{
GetterFunction getterWrapper = [propertyGetter](const Datum& thisDatum) -> AZ::Outcome<Datum, AZStd::string>
{
auto thisObject = thisDatum.GetAs<GetterType>();
if (!thisObject)
{
return AZ::Failure(AZStd::string::format("Unable to invoke getter. The getter parameter is nullptr"));
}
return AZ::Success(Datum(AZStd::invoke(propertyGetter, thisObject)));
};
return { getterWrapper, Data::FromAZType<PropertyType>(), propertyName };
}
};
template<typename FunctionType, typename SetterType, typename PropertyType>
struct WrapSetterHelper
{
static_assert(!AZStd::is_void<SetterType>::value, "Setter function must be either a member function pointer that accepts 1 arguments or an invokable object that accepts 2 argument");
static_assert(!AZStd::is_void<PropertyType>::value, "Property being set must be a non-void type");
static SetterWrapper Callback(AZStd::string_view propertyName, const FunctionType& propertySetter)
{
SetterFunction setterWrapper = [propertySetter](Datum& thisDatum, const Datum& propertyDatum) -> AZ::Outcome<void, AZStd::string>
{
auto thisObject = thisDatum.ModAs<SetterType>();
auto propertyObject = propertyDatum.GetAs<AZStd::decay_t<PropertyType>>();
if (!thisObject)
{
return AZ::Failure(AZStd::string::format("Unable to invoke setter. The setter parameter is nullptr"));
}
if (!propertyObject)
{
return AZ::Failure(AZStd::string::format("Unable to invoke setter. The property value parameter is nullptr"));
}
AZStd::invoke(propertySetter, thisObject, *propertyObject);
return AZ::Success();
};
return { setterWrapper, Data::FromAZType<PropertyType>(), propertyName };
}
};
}
template<typename FunctionType, typename = void>
struct WrapGetter;
template<typename FunctionType>
struct WrapGetter<FunctionType, AZStd::enable_if_t<AZStd::is_member_pointer<FunctionType>::value>>
: Internal::WrapGetterHelper<FunctionType, typename AZStd::function_traits<FunctionType>::class_type>
{};
template<typename FunctionType>
struct WrapGetter<FunctionType, AZStd::enable_if_t<!AZStd::is_member_pointer<FunctionType>::value && AZStd::function_traits<FunctionType>::arity == 1>>
: Internal::WrapGetterHelper<FunctionType, AZStd::decay_t<AZStd::function_traits_get_arg_t<FunctionType, 0>>>
{};
template<typename FunctionType, typename = void>
struct WrapSetter;
template<typename FunctionType>
struct WrapSetter<FunctionType, AZStd::enable_if_t<AZStd::is_member_function_pointer<FunctionType>::value && AZStd::function_traits<FunctionType>::arity == 1>>
: Internal::WrapSetterHelper<FunctionType, typename AZStd::function_traits<FunctionType>::class_type, AZStd::function_traits_get_arg_t<FunctionType, 0>>
{};
template<typename FunctionType>
struct WrapSetter<FunctionType, AZStd::enable_if_t<!AZStd::is_member_function_pointer<FunctionType>::value && AZStd::function_traits<FunctionType>::arity == 2>>
: Internal::WrapSetterHelper<FunctionType, AZStd::function_traits_get_arg_t<FunctionType, 0>, AZStd::function_traits_get_arg_t<FunctionType, 1>>
{};
template<eType>
struct PropertyTraits
{
static GetterContainer GetGetterWrappers([[maybe_unused]] const Data::Type& type)
{
return GetterContainer{};
}
static SetterContainer GetSetterWrappers([[maybe_unused]] const Data::Type& type)
{
return SetterContainer{};
}
};
template<>
struct PropertyTraits<Data::eType::Quaternion>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("x", WrapGetter<decltype(&Data::QuaternionType::GetX)>::Callback("x", &Data::QuaternionType::GetX));
getterFunctions.emplace("y", WrapGetter<decltype(&Data::QuaternionType::GetY)>::Callback("y", &Data::QuaternionType::GetY));
getterFunctions.emplace("z", WrapGetter<decltype(&Data::QuaternionType::GetZ)>::Callback("z", &Data::QuaternionType::GetZ));
getterFunctions.emplace("w", WrapGetter<decltype(&Data::QuaternionType::GetW)>::Callback("w", &Data::QuaternionType::GetW));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("x", WrapSetter<decltype(&Data::QuaternionType::SetX)>::Callback("x", &Data::QuaternionType::SetX));
setterFunctions.emplace("y", WrapSetter<decltype(&Data::QuaternionType::SetY)>::Callback("y", &Data::QuaternionType::SetY));
setterFunctions.emplace("z", WrapSetter<decltype(&Data::QuaternionType::SetZ)>::Callback("z", &Data::QuaternionType::SetZ));
setterFunctions.emplace("w", WrapSetter<decltype(&Data::QuaternionType::SetW)>::Callback("w", &Data::QuaternionType::SetW));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::Vector2>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("x", WrapGetter<decltype(&Data::Vector2Type::GetX)>::Callback("x", &Data::Vector2Type::GetX));
getterFunctions.emplace("y", WrapGetter<decltype(&Data::Vector2Type::GetY)>::Callback("y", &Data::Vector2Type::GetY));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("x", WrapSetter<decltype(&Data::Vector2Type::SetX)>::Callback("x", &Data::Vector2Type::SetX));
setterFunctions.emplace("y", WrapSetter<decltype(&Data::Vector2Type::SetY)>::Callback("y", &Data::Vector2Type::SetY));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::Vector3>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("x", WrapGetter<decltype(&Data::Vector3Type::GetX)>::Callback("x", &Data::Vector3Type::GetX));
getterFunctions.emplace("y", WrapGetter<decltype(&Data::Vector3Type::GetY)>::Callback("y", &Data::Vector3Type::GetY));
getterFunctions.emplace("z", WrapGetter<decltype(&Data::Vector3Type::GetZ)>::Callback("z", &Data::Vector3Type::GetZ));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("x", WrapSetter<decltype(&Data::Vector3Type::SetX)>::Callback("x", &Data::Vector3Type::SetX));
setterFunctions.emplace("y", WrapSetter<decltype(&Data::Vector3Type::SetY)>::Callback("y", &Data::Vector3Type::SetY));
setterFunctions.emplace("z", WrapSetter<decltype(&Data::Vector3Type::SetZ)>::Callback("z", &Data::Vector3Type::SetZ));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::Vector4>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("x", WrapGetter<decltype(&Data::Vector4Type::GetX)>::Callback("x", &Data::Vector4Type::GetX));
getterFunctions.emplace("y", WrapGetter<decltype(&Data::Vector4Type::GetY)>::Callback("y", &Data::Vector4Type::GetY));
getterFunctions.emplace("z", WrapGetter<decltype(&Data::Vector4Type::GetZ)>::Callback("z", &Data::Vector4Type::GetZ));
getterFunctions.emplace("w", WrapGetter<decltype(&Data::Vector4Type::GetW)>::Callback("w", &Data::Vector4Type::GetW));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("x", WrapSetter<decltype(&Data::Vector4Type::SetX)>::Callback("x", &Data::Vector4Type::SetX));
setterFunctions.emplace("y", WrapSetter<decltype(&Data::Vector4Type::SetY)>::Callback("y", &Data::Vector4Type::SetY));
setterFunctions.emplace("z", WrapSetter<decltype(&Data::Vector4Type::SetZ)>::Callback("z", &Data::Vector4Type::SetZ));
setterFunctions.emplace("w", WrapSetter<decltype(&Data::Vector4Type::SetW)>::Callback("w", &Data::Vector4Type::SetW));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::Color>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("r", WrapGetter<decltype(&Data::ColorType::GetR)>::Callback("r", &Data::ColorType::GetR));
getterFunctions.emplace("g", WrapGetter<decltype(&Data::ColorType::GetG)>::Callback("g", &Data::ColorType::GetG));
getterFunctions.emplace("b", WrapGetter<decltype(&Data::ColorType::GetB)>::Callback("b", &Data::ColorType::GetB));
getterFunctions.emplace("a", WrapGetter<decltype(&Data::ColorType::GetA)>::Callback("a", &Data::ColorType::GetA));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("r", WrapSetter<decltype(&Data::ColorType::SetR)>::Callback("r", &Data::ColorType::SetR));
setterFunctions.emplace("g", WrapSetter<decltype(&Data::ColorType::SetG)>::Callback("g", &Data::ColorType::SetG));
setterFunctions.emplace("b", WrapSetter<decltype(&Data::ColorType::SetB)>::Callback("b", &Data::ColorType::SetB));
setterFunctions.emplace("a", WrapSetter<decltype(&Data::ColorType::SetA)>::Callback("a", &Data::ColorType::SetA));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::Plane>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("normal", WrapGetter<decltype(&Data::PlaneType::GetNormal)>::Callback("normal", &Data::PlaneType::GetNormal));
getterFunctions.emplace("distance", WrapGetter<decltype(&Data::PlaneType::GetDistance)>::Callback("distance", &Data::PlaneType::GetDistance));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("normal", WrapSetter<decltype(&Data::PlaneType::SetNormal)>::Callback("normal", &Data::PlaneType::SetNormal));
setterFunctions.emplace("distance", WrapSetter<decltype(&Data::PlaneType::SetDistance)>::Callback("distance", &Data::PlaneType::SetDistance));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::Transform>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("basisX", WrapGetter<decltype(&Data::TransformType::GetBasisX)>::Callback("basisX", &Data::TransformType::GetBasisX));
getterFunctions.emplace("basisY", WrapGetter<decltype(&Data::TransformType::GetBasisY)>::Callback("basisY", &Data::TransformType::GetBasisY));
getterFunctions.emplace("basisZ", WrapGetter<decltype(&Data::TransformType::GetBasisZ)>::Callback("basisZ", &Data::TransformType::GetBasisZ));
getterFunctions.emplace("translation", WrapGetter<decltype(&Data::TransformType::GetTranslation)>::Callback("translation", &Data::TransformType::GetTranslation));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("translation", WrapSetter<void(AZ::Transform::*)(const AZ::Vector3&)>::Callback("translation", &Data::TransformType::SetTranslation));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::AABB>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("min", WrapGetter<decltype(&Data::AABBType::GetMin)>::Callback("min", &Data::AABBType::GetMin));
getterFunctions.emplace("max", WrapGetter<decltype(&Data::AABBType::GetMax)>::Callback("max", &Data::AABBType::GetMax));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("min", WrapSetter<decltype(&Data::AABBType::SetMin)>::Callback("min", &Data::AABBType::SetMin));
setterFunctions.emplace("max", WrapSetter<decltype(&Data::AABBType::SetMax)>::Callback("max", &Data::AABBType::SetMax));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::OBB>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("axisX", WrapGetter<decltype(&Data::OBBType::GetAxisX)>::Callback("axisX", &Data::OBBType::GetAxisX));
getterFunctions.emplace("axisY", WrapGetter<decltype(&Data::OBBType::GetAxisY)>::Callback("axisY", &Data::OBBType::GetAxisY));
getterFunctions.emplace("axisZ", WrapGetter<decltype(&Data::OBBType::GetAxisZ)>::Callback("axisZ", &Data::OBBType::GetAxisZ));
getterFunctions.emplace("halfLengthX", WrapGetter<decltype(&Data::OBBType::GetHalfLengthX)>::Callback("halfLengthX", &Data::OBBType::GetHalfLengthX));
getterFunctions.emplace("halfLengthY", WrapGetter<decltype(&Data::OBBType::GetHalfLengthY)>::Callback("halfLengthY", &Data::OBBType::GetHalfLengthY));
getterFunctions.emplace("halfLengthZ", WrapGetter<decltype(&Data::OBBType::GetHalfLengthZ)>::Callback("halfLengthZ", &Data::OBBType::GetHalfLengthZ));
getterFunctions.emplace("position", WrapGetter<decltype(&Data::OBBType::GetPosition)>::Callback("position", &Data::OBBType::GetPosition));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("halfLengthX", WrapSetter<decltype(&Data::OBBType::SetHalfLengthX)>::Callback("halfLengthX", &Data::OBBType::SetHalfLengthX));
setterFunctions.emplace("halfLengthY", WrapSetter<decltype(&Data::OBBType::SetHalfLengthY)>::Callback("halfLengthY", &Data::OBBType::SetHalfLengthY));
setterFunctions.emplace("halfLengthZ", WrapSetter<decltype(&Data::OBBType::SetHalfLengthZ)>::Callback("halfLengthZ", &Data::OBBType::SetHalfLengthZ));
setterFunctions.emplace("position", WrapSetter<decltype(&Data::OBBType::SetPosition)>::Callback("position", &Data::OBBType::SetPosition));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::Matrix3x3>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("basisX", WrapGetter<decltype(&Data::Matrix3x3Type::GetBasisX)>::Callback("basisX", &Data::Matrix3x3Type::GetBasisX));
getterFunctions.emplace("basisY", WrapGetter<decltype(&Data::Matrix3x3Type::GetBasisY)>::Callback("basisY", &Data::Matrix3x3Type::GetBasisY));
getterFunctions.emplace("basisZ", WrapGetter<decltype(&Data::Matrix3x3Type::GetBasisZ)>::Callback("basisZ", &Data::Matrix3x3Type::GetBasisZ));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("basisX", WrapSetter<void(AZ::Matrix3x3::*)(const AZ::Vector3&)>::Callback("basisX", &Data::Matrix3x3Type::SetBasisX));
setterFunctions.emplace("basisY", WrapSetter<void(AZ::Matrix3x3::*)(const AZ::Vector3&)>::Callback("basisY", &Data::Matrix3x3Type::SetBasisY));
setterFunctions.emplace("basisZ", WrapSetter<void(AZ::Matrix3x3::*)(const AZ::Vector3&)>::Callback("basisZ", &Data::Matrix3x3Type::SetBasisZ));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::Matrix4x4>
{
static GetterContainer GetGetterWrappers(const Data::Type&)
{
GetterContainer getterFunctions;
getterFunctions.emplace("basisX", WrapGetter<decltype(&Data::Matrix4x4Type::GetBasisX)>::Callback("basisX", &Data::Matrix4x4Type::GetBasisX));
getterFunctions.emplace("basisY", WrapGetter<decltype(&Data::Matrix4x4Type::GetBasisY)>::Callback("basisY", &Data::Matrix4x4Type::GetBasisY));
getterFunctions.emplace("basisZ", WrapGetter<decltype(&Data::Matrix4x4Type::GetBasisZ)>::Callback("basisZ", &Data::Matrix4x4Type::GetBasisZ));
getterFunctions.emplace("translation", WrapGetter<decltype(&Data::Matrix4x4Type::GetTranslation)>::Callback("translation", &Data::Matrix4x4Type::GetTranslation));
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type&)
{
SetterContainer setterFunctions;
setterFunctions.emplace("basisX", WrapSetter<void(AZ::Matrix4x4::*)(const AZ::Vector4&)>::Callback("basisX", &Data::Matrix4x4Type::SetBasisX));
setterFunctions.emplace("basisY", WrapSetter<void(AZ::Matrix4x4::*)(const AZ::Vector4&)>::Callback("basisY", &Data::Matrix4x4Type::SetBasisY));
setterFunctions.emplace("basisZ", WrapSetter<void(AZ::Matrix4x4::*)(const AZ::Vector4&)>::Callback("basisZ", &Data::Matrix4x4Type::SetBasisZ));
setterFunctions.emplace("translation", WrapSetter<void(AZ::Matrix4x4::*)(const AZ::Vector3&)>::Callback("translation", &Data::Matrix4x4Type::SetTranslation));
return setterFunctions;
}
};
template<>
struct PropertyTraits<Data::eType::BehaviorContextObject>
{
static GetterContainer GetGetterWrappers(const Data::Type& type)
{
GetterContainer getterFunctions;
auto behaviorProperties = GetBehaviorProperties(type);
for (auto&& behaviorProperty : behaviorProperties)
{
AZ::BehaviorMethod* getterMethod = behaviorProperty->m_getter;
if (getterMethod && getterMethod->HasResult() && getterMethod->GetNumArguments() == 1)
{
GetterFunction getterFunction = [&behaviorProperty, getterMethod](const Datum& thisDatum) -> AZ::Outcome<Datum, AZStd::string>
{
const size_t maxGetterArguments = 1;
AZStd::array<AZ::BehaviorValueParameter, maxGetterArguments> getterParams;
const AZ::BehaviorParameter* thisParam = getterMethod->GetArgument(0);
AZ::Outcome<AZ::BehaviorValueParameter, AZStd::string> thisObjectParam = thisDatum.ToBehaviorValueParameter(*thisParam);
if (!thisObjectParam.IsSuccess())
{
return AZ::Failure(AZStd::string::format("BehaviorContextObject %s couldn't be turned into a BehaviorValueParameter for getter: %s",
Data::GetName(thisDatum.GetType()).data(),
thisObjectParam.GetError().data()));
}
getterParams[0].Set(thisObjectParam.TakeValue());
auto behaviorMethodInvokeOutcome = Datum::CallBehaviorContextMethodResult(getterMethod, getterMethod->GetResult(), getterParams.begin(),
static_cast<AZ::u32>(getterMethod->GetNumArguments()), behaviorProperty->m_name);
if (!behaviorMethodInvokeOutcome)
{
return AZ::Failure(AZStd::string::format("Attempting to invoke getter method %s failed: %s", getterMethod->m_name.data(), behaviorMethodInvokeOutcome.GetError().data()));
}
return behaviorMethodInvokeOutcome;
};
const AZ::BehaviorParameter* resultParam = getterMethod->GetResult();
Data::Type resultType = Data::FromAZType(resultParam->m_typeId);
if (AZ::BehaviorContextHelper::IsStringParameter((*resultParam)))
{
resultType = Data::Type::String();
}
getterFunctions.insert({ behaviorProperty->m_name, { AZStd::move(getterFunction), resultType, behaviorProperty->m_name } });
}
}
return getterFunctions;
}
static SetterContainer GetSetterWrappers(const Data::Type& type)
{
enum : size_t { thisIndex = 0U };
enum : size_t { propertyIndex = 1U };
SetterContainer setterFunctions;
auto behaviorProperties = GetBehaviorProperties(type);
for (auto&& behaviorProperty : behaviorProperties)
{
AZ::BehaviorMethod* setterMethod = behaviorProperty->m_setter;
if (setterMethod && setterMethod->GetNumArguments() == 2)
{
SetterFunction setterFunction = [setterMethod](Datum& thisDatum, const Datum& propertyDatum) -> AZ::Outcome<void, AZStd::string>
{
const size_t maxSetterArguments = 2;
AZStd::array<AZ::BehaviorValueParameter, maxSetterArguments> setterParams;
const AZ::BehaviorParameter* thisParam = setterMethod->GetArgument(thisIndex);
AZ::Outcome<AZ::BehaviorValueParameter, AZStd::string> thisObjectParam = thisDatum.ToBehaviorValueParameter(*thisParam);
if (!thisObjectParam.IsSuccess())
{
return AZ::Failure(AZStd::string::format("BehaviorContextObject %s couldn't be turned into a BehaviorValueParameter for setter: %s",
Data::GetName(thisDatum.GetType()).data(),
thisObjectParam.GetError().data()));
}
setterParams[thisIndex].Set(thisObjectParam.TakeValue());
const AZ::BehaviorParameter* propertyParam = setterMethod->GetArgument(propertyIndex);
AZ::Outcome<AZ::BehaviorValueParameter, AZStd::string> propertyObjectParam = propertyDatum.ToBehaviorValueParameter(*propertyParam);
if (!propertyObjectParam.IsSuccess())
{
return AZ::Failure(AZStd::string::format("Property type %s couldn't be turned into a BehaviorValueParameter for setter. BehaviorContextObject %s will not be set: %s.",
Data::GetName(propertyDatum.GetType()).data(),
Data::GetName(thisDatum.GetType()).data(),
propertyObjectParam.GetError().data()));
}
setterParams[propertyIndex].Set(propertyObjectParam.GetValue());
if (!setterMethod->Call(setterParams.data(), static_cast<AZ::u32>(setterParams.size())))
{
return AZ::Failure(AZStd::string::format("Attempting to invoke setter method %s failed", setterMethod->m_name.data()));
}
return AZ::Success();
};
const AZ::BehaviorParameter* argumentParam = setterMethod->GetArgument(propertyIndex);
Data::Type argumentType = Data::FromAZType(argumentParam->m_typeId);
if (AZ::BehaviorContextHelper::IsStringParameter((*argumentParam)))
{
argumentType = Data::Type::String();
}
setterFunctions.insert({ behaviorProperty->m_name, { AZStd::move(setterFunction), argumentType, behaviorProperty->m_name } });
}
}
return setterFunctions;
}
};
namespace Properties
{
struct TypeErasedPropertyTraits
{
AZ_CLASS_ALLOCATOR(TypeErasedPropertyTraits, AZ::SystemAllocator, 0);
TypeErasedPropertyTraits() = default;
template<typename Traits>
explicit TypeErasedPropertyTraits(Traits)
{
m_getterFunctionCB = &Traits::GetGetterWrappers;
m_setterFunctionCB = &Traits::GetSetterWrappers;
}
bool m_isTransient = false;
GetterContainer GetGetterWrappers(const Data::Type& type) const { return m_getterFunctionCB ? m_getterFunctionCB(type) : GetterContainer{}; }
SetterContainer GetSetterWrappers(const Data::Type& type) const { return m_setterFunctionCB ? m_setterFunctionCB(type) : SetterContainer{}; }
using GetterContainerCreator = GetterContainer(*)(const Data::Type&);
GetterContainerCreator m_getterFunctionCB = nullptr;
using SetterContainerCreator = SetterContainer(*)(const Data::Type&);
SetterContainerCreator m_setterFunctionCB = nullptr;
};
template<eType scTypeValue>
static TypeErasedPropertyTraits MakeTypeErasedPropertyTraits()
{
return TypeErasedPropertyTraits(PropertyTraits<scTypeValue>{});
}
}
}
}