Skip to content

Commit

Permalink
vt: limit VtGetKnownValueTypeIndex to known types
Browse files Browse the repository at this point in the history
Split known value type index generation into public & detail
functions, where the public function, VtGetKnownValueTypeIndex, now
issues a compile-time error when querying a type not known to Vt.
This makes it safer to use with switch statements because returning -1
for unknown types would generate a case label that appears to match a
specific type but actually matches any unknown type.

    switch (val.GetKnownValueTypeIndex()) {
    case VtGetKnownValueTypeIndex<double>(): // OK: double is known to Vt.
        val.Cast<float>();
        break;
    case VtGetKnownValueTypeIndex<MyType>(): // Error: MyType is not known.
        val.Cast<MyOtherType>();             //        `val` may be empty or
        break;                               //        hold some other type.
    }

VtIsKnownValueType() allows metaprograms to determine whether a type
is in VT_VALUE_TYPES or not.

Interally, VtValue uses Vt_KnownValueTypeDetail::GetIndex() to capture
the held type's index, which returns -1 for types that are not known.

(Internal change: 2258463)
  • Loading branch information
jloy authored and pixar-oss committed Jan 4, 2023
1 parent f1049ff commit af9a9d3
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 6 deletions.
41 changes: 41 additions & 0 deletions pxr/base/vt/testenv/testVtCpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,46 @@ testVisitValue()

}

template <typename T>
static void
AssertIsHoldingKnownType(const VtValue &val)
{
switch (val.GetKnownValueTypeIndex()) {
case VtGetKnownValueTypeIndex<T>():
break;
default:
TF_FATAL_ERROR("Expected %s (index=%d); got index %d",
ArchGetDemangled<T>().c_str(),
VtGetKnownValueTypeIndex<T>(),
val.GetKnownValueTypeIndex());
}
}

struct TypeNotKnownToVt {};

static void
testKnownValueTypeIndex()
{
VtValue iv(123);
VtValue dv(1.23);
VtValue fv(2.34f);
VtValue hv(GfHalf(3.45));
VtValue sv(std::string("hello"));
VtValue av(VtArray<float>(123));

AssertIsHoldingKnownType<int>(iv);
AssertIsHoldingKnownType<double>(dv);
AssertIsHoldingKnownType<float>(fv);
AssertIsHoldingKnownType<GfHalf>(hv);
AssertIsHoldingKnownType<std::string>(sv);
AssertIsHoldingKnownType<VtArray<float>>(av);

TF_AXIOM(VtIsKnownValueType<int>());
TF_AXIOM(VtIsKnownValueType<VtArray<GfVec3d>>());
TF_AXIOM(!VtIsKnownValueType<void>());
TF_AXIOM(!VtIsKnownValueType<TypeNotKnownToVt>());
}

int main(int argc, char *argv[])
{
testArray();
Expand All @@ -1784,6 +1824,7 @@ int main(int argc, char *argv[])
testCombinedVtValueProxies();

testVisitValue();
testKnownValueTypeIndex();

printf("Test SUCCEEDED\n");

Expand Down
42 changes: 37 additions & 5 deletions pxr/base/vt/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,32 +190,64 @@ VT_ARRAY_VALUE_TYPES VT_SCALAR_CLASS_VALUE_TYPES VT_NONARRAY_VALUE_TYPES
#define VT_VALUE_TYPES \
VT_BUILTIN_VALUE_TYPES VT_CLASS_VALUE_TYPES

// Provide compile-time value type indexes for types that are "known" to Vt --
// specifically, those types that appear in VT_VALUE_TYPES. Note that VtArray
// and VtValue can work with other types that are not these "known" types.
namespace Vt_KnownValueTypeDetail
{

// Implement compile-time value type indexes.
//
// Base case -- unknown types get index -1.
template <class T>
constexpr int
VtGetKnownValueTypeIndex() {
GetIndex() {
return -1;
}

// Set indexes for known types.
#define VT_SET_VALUE_TYPE_INDEX(r, unused, i, elem) \
template <> constexpr int \
VtGetKnownValueTypeIndex< VT_TYPE(elem) >() { \
GetIndex< VT_TYPE(elem) >() { \
return i; \
}
BOOST_PP_SEQ_FOR_EACH_I(VT_SET_VALUE_TYPE_INDEX, ~, VT_VALUE_TYPES)
#undef VT_SET_VALUE_TYPE_INDEX

} // Vt_KnownValueTypeDetail

// Total number of 'known' value types.
constexpr int
VtGetNumKnownValueTypes() {
return BOOST_PP_SEQ_SIZE(VT_VALUE_TYPES);
}

/// Provide compile-time value type indexes for types that are "known" to Vt --
/// specifically, those types that appear in VT_VALUE_TYPES. Note that VtArray
/// and VtValue can work with other types that are not these "known" types.
///
/// VtGetKnownValueTypeIndex can only be used with known types. Querying a
/// type that is not known to Vt results in a compilation error. The set of
/// known types and their indexes are not guaranteed to be stable across
/// releases of the library.
///
/// Most clients should prefer VtVisitValue over direct use of the type index
/// as VtVisitValue provides convenient and efficient access to the held
/// value.
template <class T>
constexpr int
VtGetKnownValueTypeIndex()
{
constexpr int index = Vt_KnownValueTypeDetail::GetIndex<T>();
static_assert(index != -1, "T is not one of the known VT_VALUE_TYPES.");
return index;
}

/// Returns true if `T` is a type that appears in VT_VALUE_TYPES.
template <class T>
constexpr bool
VtIsKnownValueType()
{
return Vt_KnownValueTypeDetail::GetIndex<T>() != -1;
}

// None of the VT_VALUE_TYPES are value proxies. We want to specialize these
// templates here, since otherwise the VtIsTypedValueProxy will require a
// complete type to check if it derives VtTypedValueProxyBase.
Expand Down
2 changes: 1 addition & 1 deletion pxr/base/vt/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ class VtValue
constexpr _TypeInfoImpl()
: _TypeInfo(typeid(T),
_ArrayHelper<T>::GetElementTypeid(),
VtGetKnownValueTypeIndex<T>(),
Vt_KnownValueTypeDetail::GetIndex<T>(),
VtIsArray<T>::value,
VtIsHashable<T>(),
IsProxy,
Expand Down

0 comments on commit af9a9d3

Please sign in to comment.