-
Notifications
You must be signed in to change notification settings - Fork 27
Open
Description
Currently ACT does not support generic classes (aka template classes) which makes it hard to define certain types such as (type-safe) generic containers and algorithms.
For example, to define types like Map<int,IFoo*>
, Map<string,IBar*>
, and Map<int,double>
in the API today, you would need to define the following:
<class name="Map" abstract="true">
<!-- Base methods that do not use the value type (Size, IsEmpty, etc.) -->
</class>
<class name="IntFooMap" parent="Map">
<!-- Methods with key type="int32" and value type="class" class="Foo" -->
</class>
<class name="StringBarMap">
<!-- Same methods copy-pasted, but with key type="string" and value type="class" class="Bar" -->
</class>
<class name="IntDoubleMap">
<!-- Same methods copy-pasted, but with key type="int32" and value type="double" -->
</class>
This quickly becomes unmaintainable as for N instantiations you need to:
- Copy-paste and adjust the method definitions N times
- Maintain N implementation classes by:
- Writing the same code N times, or
- Writing a generic
Map
class yourself and inheriting from that N times (something likeclass StringBarMap : public Map<std::string, IBar*>
)
Instead, ACT could provide support for generic classes, and it could look something like this (following previous discussions with @martinweismann and @alexanderoster):
<templateclass name="Map">
<!--
template<typename TKey, typename TValue>
-->
<templateparam name="TKey" description="" />
<templateparam name="TValue" description="" />
<!-- template methods -->
<templatemethod name="Add" description="adds an entry to the map.">
<param name="Key" type="template" templateparam="TKey" pass="in" description="Key String."/>
<param name="Value" type="template" templateparam="TValue" pass="in" description="Value to store."/>
</templatemethod>
<templatemethod name="Get" description="returns an entry of the map.">
<param name="Key" type="template" templateparam="TKey" pass="in" description="Key String."/>
<param name="Instance" type="template" templateparam="TValue" pass="return" description="Class Instance."/>
</templatemethod>
<templatemethod name="GetOut" description="returns an entry of the map.">
<param name="Key" type="template" templateparam="TKey" pass="in" description="Key String."/>
<param name="Instance" type="template" templateparam="TValue" pass="out" description="Class Instance."/>
</templatemethod>
<!-- non-template methods -->
<method name="Clear" description="clears the map." />
<method name="Count" description="returns the entry count of the map.">
<param name="Count" type="uint32" pass="return" description="Entry count of the map."/>
</method>
</templateclass>
<class name="IntFooMap" parent="Map">
<templatearg param="TKey" type="int32"/>
<templatearg param="TValue" type="class" class="Foo" />
</class>
<class name="StringBarMap" parent="Map">
<templatearg param="TKey" type="string"/>
<templatearg param="TValue" type="class" class="Bar" />
</class>
<class name="IntDoubleMap" parent="Map">
<templatearg param="TKey" type="int32"/>
<templatearg param="TValue" type="double" />
</class>
The implementation stubs could then be generated as follows:
// Implementation Stubs
namespace Component { namespace Impl {
template <typename TKey, typename TValue>
class CMap
{
public:
virtual void Add(TKey Key, TValue Value);
virtual TValue Get(TKey Key);
virtual void GetOut(TKey Key, TValue *pValue);
virtual void Clear();
virtual Component_uint32 Count();
};
class IntFooMap : public CMap<Component_int32, IFoo *>
{
/* instantiates to:
virtual void Add(Component_int32 Key, IFoo * Value);
virtual IFoo * Get(Component_int32 Key);
virtual void GetOut(Component_int32 Key, IFoo **pValue);
virtual void Clear();
virtual Component_uint32 Count();
*/
};
class StringBarMap : public CMap<String *, IBar *>
{
/* instantiates to:
virtual void Add(String * Key, IBar * Value);
virtual IBar * Get(String * Key);
virtual void GetOut(String * Key, IBar **pValue);
virtual void Clear();
virtual Component_uint32 Count();
*/
};
class IntDoubleMap : public CMap<Component_int32, double>
{
/* instantiates to:
virtual void Add(Component_int32 Key, double Value);
virtual double Get(Component_int32 Key);
virtual void GetOut(Component_int32 Key, double *pValue);
virtual void Clear();
virtual Component_uint32 Count();
*/
};
}} // namespace Component::Impl
And the bindings:
// Bindings
namespace Component { namespace Binding {
class CMap
{
public:
virtual void Clear();
virtual Component_uint32 Count();
};
class IntFooMap : public CMap
{
virtual void Add(Component_int32 Key, IFoo * Value);
virtual IFoo * Get(Component_int32 Key);
virtual void GetOut(Component_int32 Key, IFoo **pValue);
};
class StringBarMap : public CMap
{
virtual void Add(String * Key, IBar * Value);
virtual IBar * Get(String * Key);
virtual void GetOut(String * Key, IBar **pValue);
};
class IntDoubleMap : public CMap
{
virtual void Add(Component_int32 Key, double Value);
virtual double Get(Component_int32 Key);
virtual void GetOut(Component_int32 Key, double *pValue);
};
}} // namespace Component::Binding
The API author would then only need to implement the template class CMap once.
Notes:
- Only works with strings if they're wrapped in a class
- Already a need for string wrapper class
- Can't support string without wrapper class because of different in/out/return types (
const std::string&
/std::string &
/std::string
)- Template class signatures would differ from the simple / class types, won't work.
- Ref counting, need to check if type is a pointer or not to decide whether or not to call IncRefCount/DecRefCount.
- Provide a helper function with specializations for class / simple types
- Concepts
- Example: key type for map, how to compare?
pLhs->Compare(pRhs)
?pLhs < pRhs
?
- Leave it up to the API author, C++ (pre C++20) didn't have support for Concepts either and relied on documentation
- API author can choose to write template functions with specializations like (ACT could document an example)
template <typename T, typename = void> struct compare_helper { static int compare(T lhs, T rhs); }; template <typename Comparable> struct compare_helper<Comparable, std::enable_if_t<std::is_base_of_v<IBase, std::remove_pointer_t<Comparable>>>> { static int compare(Comparable pLhs, Comparable pRhs) { static_assert(std::is_member_function_pointer<decltype(&std::remove_pointer_t<Comparable>::Compare)>::value, "Type does not implement Comparable concept (Comparable::Compare is not a member function)."); return pLhs->Compare(pRhs); } }; template <typename Comparable> struct compare_helper<Comparable, std::enable_if_t<std::is_arithmetic_v<Comparable>>> { static int compare(Comparable lhs, Comparable rhs) { return lhs - rhs; } }; template <typename T> int compare(T lhs, T rhs) { return compare_helper<T>::compare(lhs,rhs); } // compare(pObject1, pObject2) => compiles only if pObject1 has Compare method // compare(1,2)
- API author can choose to write template functions with specializations like (ACT could document an example)
- Example: key type for map, how to compare?
Metadata
Metadata
Assignees
Labels
No labels