IntroductionWhat this article presents is implementing in C++ the C# functionality for both Property and Indexer and controlling their compile-time accessibility using accessor-modifiers.
The provided source code contains a generic framework for performing both Property and Indexer in C++. The only thing that the user of this framework needs to implement is an Accessor functor, which requires set and get accessor functions. The framework provides pre-defined properties, each with different accessibility options, which will wrap around the user-defined Accessor functor. Thereby, to declare a property within a Host class is simply choosing a property based upon expected accessibility and assigning it with an Accessor.
Properties are known as smart fields, and enable access to member variables of a class, but maintains encapsulation by implicit access to its set and get accessors.
Indexers are also called smart arrays in C#, and can be used to use an object as an array. Similar to C# Properties, the subscript ([]) operator has access to its own set and get accessors.
BackgroundMy programming lingua franca has been C++ for quite a while, and thereby it is my foundation for other programming languages like Java and C#. Every time I see some feature with these other languages, I am curious enough to query if that feature could be implemented in C++. C# Properties and Indexers is a case in point.
In CodeProject, I have read several articles on C++ implementation of C# Property, and none pertaining to C# Indexer. However, I did not find an article that provided compile-time check of declared accessibility of a Property's field.
The code presented in this article provides Property and Indexer functionality in C++, including compile-time check of declared accessibility of the assigned accessor-modifier for set and get accessors.
C++ Property LayoutCollaboration ModelIn this C++ implementation of C# Property, there are three components:
- Accessor defines how values are managed by the set or get accessor member functions. The value can either reside within the scope of the Accessor or within the scope of its friend Host.
- Property is a container of a single reference to an Accessor, and it defines the access scope (public or private) to the Accessor's set and get member functions through its access functions (i.e., assignment and cast operators).
- Host is a container which may reference one or more Properties. Host is a friend of both Property and Accessor. The purpose of this friendship is if either of the Property access functions (i.e., assignment and cast operators) are declared private to those outside the scope of the Host class, the Host can still access the value within its internal scope, directly through its referenced Property, or indirectly through the Property's referenced Accessor.
Call ModelListed here is the call model of this C++ implementation, from assigning to acquiring values through a C# Property.
- Value assignments to a Property field are performed through its assignment operator.
- If the access-modifier for this operator is public, then any entity outside the scope of the Host class can make the property's value assignments.
- If the access-modifier for this operator is private, then only the Host can make the property's value assignments.
- All Property value assignments are passed to the Accessor's public set member function.
- The set accessor passes the value to any assigned resource. The assigned resource can either be within the scope of the Accessor or within the scope of its friend Host.
- Value retrievals from a property field are performed through a cast operator.
- If the access-modifier for this operator is public, then any entity outside the scope of the Host class can request the property's value.
- If the access-modifier for this operator is private, then only the Host can request the property's value.
- All Property value requests are passed to the Accessor's public get member function.
- The get accessor requests a value from any assigned resource. As with the set accessor, the assigned resource can either be within the scope of the Accessor or within the scope of its friend Host.

Accessor Interface and Base ClassesEvery Accessor for a property must define the following functions:
- A get property accessor is used to return the property value.
- A set property accessor is used to assign a new value.
The interface IAccessor defines the expected accessor functions, and the abstract base class AccessorBaseholds the ValueType value shared by the accessor functions.
Hide Copy Code
template <class Host, typename ValueType>class IAccessor{public: virtual void set(Host*, const ValueType) = 0; virtual ValueType get(Host*) const = 0;};template <class Host, typename ValueType>class AccessorBase : public IAccessor<Host, ValueType>{public: typedef ValueType value_type;};Accessor Implementation ExampleThe class Number_Accessor is a derivation of the AccessorBase class, and has a ValueType of int. In this example implementation, the value reference for the set and get accessors is within the scope of this class: private int m_value.
Hide Copy Code
template <class Host>class Number_Accessor : public AccessorBase<Host, int>{ friend Host;public: void set(Host * /* _pHost */, const int _value) { m_value = _value; } int get(Host * /* _pHost */) const { return m_value; }private: int m_value;};Property Interface and Base ClassThe abstract class PropertyBase holds an instance of the Accessor container. PropertyBase is abstract because it does not hold implementations of its derived interface IProperty.
Hide Copy Code
template<typename ValueType>class IProperty{ virtual ValueType operator =(const ValueType& value) = 0; virtual operator ValueType() const = 0;};template<class Host, class Accessor, typename ValueType>class PropertyBase : public IProperty<ValueType>{public: PropertyBase(Host *_pHost) : m_pHost( _pHost ) {} ValueType operator =(const ValueType& _value) { m_Accessor.set(m_pHost, _value); return _value; } operator ValueType() const { return m_Accessor.get(m_pHost); }protected: Host* m_pHost; Accessor m_Accessor;};Properties with Different Access ModifiersThere are three different Property classes, and each has a different arrangement of access-modifiers to their setter and getter functions:
- Property: setter and getter functionality are both public.
- Property_Set_Public: setter is public and getter is private.
- Property_Get_Public: setter is private and getter is public.
Notice that the access modifiers for the accessor methods of the class Property are both public:
Hide Copy Code
template<class Host, class Accessor, typename ValueType>class Property : public PropertyBase<Host, Accessor, ValueType>{public: Property(Host *_pHost = NULL) : PropertyBase( _pHost ){}public: using PropertyBase<Host, Accessor, ValueType>::operator =;public: using PropertyBase<Host, Accessor, ValueType>::operator ValueType;};Host Using C++ Property ImplementationUnderstand that the purpose of the article is to provide a generic toolkit (a framework) for performing a Property within a C++ class, the only thing that the user needs to implement an Accessor.
Define AccessorTo define an Accessor, derive a class from AccessorBase and implement the expected set and get accessor functions.
Here, an accessor Number_Accessor is derived from TAccessor, which encapsulates the scope of the accessor's value of type int within.
Hide Copy Code
template <class Host, typename ValueType>struct TAccessor : public AccessorBase<Host, ValueType>{ void set(Host * /* _pHost */, const ValueType _value) { m_value = _value; } ValueType get(Host * /* _pHost */) const { return m_value; }protected: ValueType m_value;};template <class Host>struct Number_Accessor : public TAccessor<Host, int> {};Define Properties for HostWithin a Host class, each property is defined (typedef) with its expected accessibility and accessor.
To define a Property within this framework, select one of these Property classes based upon the accessibility needs: Property, Property_Set_Public, or Property_Get_Public, and assign it the expected Accessor it will represent.
In the following coding example, the class Test has defined three different Properties (each with a different accessibility scope) and all using the same accessor Number_Accessor.
Hide Copy Code
class Test{ // outside the scope of class Test, // set and get are both public typedef :
roperty< ::Test, ::Number_Accessor<::Test>, ::Number_Accessor<::Test>::value_type> Property_Number; // outside the scope of class Test, // set is public and get is private typedef :
roperty_Set_Public< ::Test, ::Number_Accessor<::Test>, ::Number_Accessor<::Test>::value_type> Property_Set_Public_Number; // outside the scope of class Test, // set is private and get is public typedef :
roperty_Get_Public< ::Test, ::Number_Accessor<::Test>, ::Number_Accessor<::Test>::value_type> Property_Get_Public_Number;public: Property_Number Number_A; Property_Set_Public_Number Number_B; Property_Get_Public_Number Number_C;};Using Host with PropertiesWhen actually using the just implemented class Test, at compile-time, you can see the setters and getters of each of the three properties (Number_A, Number_B, and Number_C</code />) which are accessible outside the scope of their Host class. Referencing the following code, notice where the compile-time errors are occurring for each of the restrictive access Properties.
Hide Shrink
Copy Code
void main(){ ::Test test; int i = 0; // Number_A: Property assignment has set and get accessors as public. { test.Number_A = 10; i = test.Number_A; } // Number_B: Property assignment has set accessor as public and get accessor as private. { test.Number_B = 10; // Error C2248: 'Property_Set_Public<Host,Accessor,ValueType>:
perator .H' : // cannot access private member declared in // class 'Property_Set_Public<Host,Accessor,ValueType>' // i = test.Number_B; } // Number_C: Property assignment has set accessor as private and get accessor as public. { // Error C2248: 'Property_Get_Public<Host,Accessor,ValueType>:
perator =' : // cannot access private member declared in // class 'Property_Get_Public<Host,Accessor,ValueType>' // test.Number_C = 10; i = test.Number_C; }}C++ Indexer LayoutCollaboration ModelIn this C++ implementation of the C# Indexer, there are three components for providing accessor functionality through a Host's subscript operator.
- IndexerAccessor is the same as a Property's Accessor, except the set and get accessors are passed the index value provided though the called subscript operator.
- Indexer is the same as a Property.
- Host may define at most one subscript operator. When a Host's subscript operator is used, it passes out an instance of a derived Indexer. Depending upon the usage of the subscript operator, assigning values is performed by the Indexer's assignment operator, and acquiring values is performed by the Indexer's cast operator.
Call ModelThe call model for the C++ implementation of a C# Indexer is essentially the same as the C# Property, except the Host's subscript operator usage is made indirectly through an instance of a derived Indexer. What is occurring is that the Indexer is being used as a functor, i.e., simply any object that is called as if it is a function providing access to the Accessor.
IndexerAccessor Interface and Base ClassesThe only difference between the interface IIndexerAccessor and the interface IAccessor is that the set and get accessor member functions require an additional parameter (index) which is provided upon usage of the Host's subscript operator.
Hide Copy Code
template <class Host, typename ValueType>class IIndexerAccessor{public: virtual void set(Host*, const int _index, const ValueType) = 0; virtual ValueType get(Host*, const int _index) const = 0;};template <class Host, typename ValueType>class IndexerAccessorBase : public IIndexerAccessor<Host, ValueType>{public: typedef ValueType value_type;};IndexerAccessor Implementation ExampleThe class Number_Indexer_Accessor is a derivation of the class IndexerAccessorBase, and has a ValueType of int. In this example implementation, the value reference for the set and get accessors is not within the scope of this class but within the scope of its Host: private int *m_ptrArray.
Hide Copy Code
template <class Host>class Numbers_Indexer_Accessor : public IndexerAccessorBase<Host, int>{ friend Host;public: void set(Host* _pHost, const int _index, const int _value) { _pHost->m_ptrArray[_index] = _value; } int get(Host* _pHost, const int _index) const { return _pHost->m_ptrArray[_index]; }};Indexers Interface and Base ClassThe abstract class IndexerBase holds an instance the IndexerAccessor container. IndexerBase is abstract because it does not hold implementations of its derived interface IIndexer.
Indexers with Different Access ModifiersAs with Properties, there are three different Indexers, and each has a different arrangement of access-modifiers to their setter and getter functions:
- Inderer: setter and getter functionality are both public.
- Indexer_Set_Public: setter is public and getter is private.
- Indexer_Get_Public: setter is private and getter is public.
Notice that the access modifiers for the accessor methods of the class Indexer are both public:
Hide Copy Code
template<class Host, class IndexAccessor, typename ValueType>class Indexer : public IndexerBase<Host,IndexAccessor,ValueType>{public: Indexer(Host *_pHost, int _index = 0) : IndexerBase( _pHost, _index) {}public: using IndexerBase<Host,IndexAccessor,ValueType>::operator =;public: using IndexerBase<Host,IndexAccessor,ValueType>::operator ValueType;};Host Using the C++ Indexer ImplementationThis is a simple example using the class Indexer with the previously implemented IndexAccessor labeled Numbers_Indexer_Accessor.
Host's Subscript Operator and IndexerThe class Host_Numbers_Indexer defines an Indexer using a Numbers_Indexer_Accessor labeled Number_Indexer. Every time the Host's subscript operator is called (for either assigning or acquiring values), an instance of Numbers_Indexer is provided, and acts as a functor for setter and getter operations.
And most importantly, notice in particular what is returned from the subscript operator of Host_Number_Indexer; which is an Indexer derived Numbers_Indexer.
Hide Shrink
Copy Code
class Host_Numbers_Indexer{ friend ::Numbers_Indexer_Accessor<::Host_Numbers_Indexer>; friend ostream &operator<<( ostream &, const Host_Numbers_Indexer & ); typedef ::Indexer < ::Host_Numbers_Indexer, ::Numbers_Indexer_Accessor<::Host_Numbers_Indexer>, ::Numbers_Indexer_Accessor<::Host_Numbers_Indexer>::value_type > Numbers_Indexer ;public: Host_Numbers_Indexer(int _sizeArray = 10) { m_sizeArray = ( _sizeArray > 0 ? _sizeArray : 10 ); m_ptrArray = new int[ m_sizeArray ]; // create space for array assert( m_ptrArray != 0 ); // terminate if memory not allocated for ( int i = 0; i < static_cast<int>(m_sizeArray); i++ ) { m_ptrArray[ i ] = i ; } } // subscript operator Host_Numbers_Indexer::Numbers_Indexer operator[]( int _index ) { return Numbers_Indexer(this, _index); }private: int *m_ptrArray; // pointer to first element of array size_t m_sizeArray; // m_sizeArray of the array };Using the Host's Indexer through the Subscript OperatorReferring to the following code, upon creating an instance of the class Host_Numbers_Indexer, it populates a private array with ten integers. The initial contents of this array are dumped. Then, through the Host's subscript operator, the Indexer passes in the integer 30 into the sixth position of the array through the IndexAccessor, and then reads it back. The modified contents of this array are dumped again.
Hide Copy Code
Host_Numbers_Indexer test;cout << "Before input: " << endl << "test: " << endl << test << endl;test[5] = 30;cout << test[5] << endl;cout << "After input: " << endl << "test: " << endl << test << endl;OutputThis is the console output of the aforementioned code.
Hide Copy Code
Before input:test: 0 1 4 9 16 25 36 49 64 8130After input:test: 0 1 4 9 16 30 36 49 64 81ConclusionBy reverse engineering the C# Property and Indexer, I have gained an even greater appreciation in the flexibility that C++ provides. Although it maybe very straightforward to use these features in C#, I found this exercise in putting together this code in C++ very engaging.