HashMap operator = cannot access private member



  • Hello,

    While implementing plugins in the past I managed to understand that for using a specific class into maxon::BaseArray one had to provide own copy and assignment functionality, instead of the default generated ones. This related to the fact that MAXON_DISALLOW_COPY_AND_ASSIGN needed to be defined.
    In R20 and above the issue I experience does not seem to exist.
    This probably due to changes in the SDK related to maxon::HashMap::Insert replacing the former maxon::HashMap::Put ... I haven't digged deeper, so it is just a thought I have.

    However, with R16 .. R19, I experience a 'TestClass::operator = ': cannot access private member declared in class 'TestClass'.

    This has been brought up quite a while, back in the days before R20, but mostly when using maxon::BaseArray.
    So, now that I am using maxon::HashMap I encounter an issue I don't seem to know the solution for.

    I do have provided the necessary constructors and operators to make it work for the maxon::BaseArray, but I seem to miss something for the maxon::HashMap. And I just seem not to be able to put my finger on ...

    #include "c4d.h"
    
    class TestClass
    {
    	MAXON_DISALLOW_COPY_AND_ASSIGN(TestClass)
    
    private:
    	Bool mBool;
    	Int32 mInt32;
    
    public:
    	TestClass() : mBool(false), mInt32(0) {}
    	~TestClass() {}
    
    	// move constructor
    	TestClass(TestClass&& src) :
    		mBool(std::move(src.mBool)),
    		mInt32(std::move(src.mInt32))
    	{}
    
    	// move assignment operator
    	MAXON_OPERATOR_MOVE_ASSIGNMENT(TestClass);
    
    	// copy
    	MAXON_WARN_UNUSED maxon::Result<void> CopyFrom(const TestClass& src)
    	{
    		mBool = src.mBool;
    		mInt32 = src.mInt32;
    		return maxon::OK;
    	}
    };
    
    void Test()
    {
    	// using BaseArray does compile
    
    	maxon::BaseArray<TestClass> arrayTestClass;
    
    	TestClass newitem;
    	arrayTestClass.Append(newitem);
    
    	// using HashMap does not compile
    	// operator = cannot acces private member
    
    	maxon::HashMap<Int32, TestClass> mapTestClass;
    
    	TestClass newitem2;
    	mapTestClass.Put(0, newitem2);
    }
    
    Error	C2248	'TestClass::operator =' : cannot access private member declared in class 'TestClass'	
    

    Now, I do understand the fact that the assignment operator is private for the hashmap data structure, and thus the reason for the error being reported. I just don't see what is missing in my TestClass?



  • Hmm ... seems I have been doing it wrong all those years ???

    According to this post in the legacy forum, the disallowing of copy and assignment isn't to be done in the class that is being used INSIDE a maxon::BaseArray (or maxon::HashMap).
    It's the class that holds such basearray or hashmap as member which will need to provide the copy and assignment implementation.

    I need to try this.



  • Still getting nowhere ...

    Following code uses only maxon::BaseArray and all compiles fine.

    #include "c4d.h"
    
    class ValuePair
    {
    private:
    	Int32 mValue1;
    	Int32 mValue2;
    
    public:
    	ValuePair() : mValue1(0), mValue2(0) {}
    	~ValuePair() {}
    };
    
    class ValuePairArray
    {
    	MAXON_DISALLOW_COPY_AND_ASSIGN(ValuePairArray)
    
    private:
    	maxon::BaseArray<ValuePair> mBaseArrayValues;
    
    public:
    	ValuePairArray() {}
    	~ValuePairArray() {}
    
    	// move constructor
    	ValuePairArray(ValuePairArray&& src) :
    		mBaseArrayValues(std::move(src.mBaseArrayValues))
    	{}
    
    	// move assignment operator
    	MAXON_OPERATOR_MOVE_ASSIGNMENT(ValuePairArray);
    
    	// copy
    	MAXON_WARN_UNUSED maxon::Result<void> CopyFrom(const ValuePairArray& src)
    	{
    		if (mBaseArrayValues.CopyFrom(src.mBaseArrayValues) == maxon::FAILED)
    			return maxon::OutOfMemoryError(CREATE);
    		return maxon::OK;
    	}
    };
    
    class StorageWrapper
    {
    	MAXON_DISALLOW_COPY_AND_ASSIGN(StorageWrapper)
    
    public:
    	StorageWrapper() {}
    	~StorageWrapper() {}
    
    	// move constructor
    	StorageWrapper(StorageWrapper&& src) :
    		mBaseArray(std::move(src.mBaseArray))
    	{}
    
    	// move assignment operator
    	MAXON_OPERATOR_MOVE_ASSIGNMENT(StorageWrapper);
    
    	// copy
    	MAXON_WARN_UNUSED maxon::Result<void> CopyFrom(const StorageWrapper& src)
    	{
    		if (mBaseArray.CopyFrom(src.mBaseArray) == maxon::FAILED)
    			return maxon::OutOfMemoryError(CREATE);
    		return maxon::OK;
    	}
    
    	maxon::BaseArray<ValuePairArray> mBaseArray;
    };
    
    void Test()
    {
    	ValuePairArray newitem;
    
    	// using the ValuePairArray class directly
    	maxon::BaseArray<ValuePairArray> arrayOfValuePairArray;
    
    	arrayOfValuePairArray.Append(newitem);
    
    
    	// using the ValuePairArray class via a storage wrapper class
    	StorageWrapper storageHelper;
    	
    	storageHelper.mBaseArray.Append(newitem);
    }
    
    

    Following code continues on the previous code, but adds a maxon::HashMap.
    And results in compiler error for the private assignment operator being inaccessible.
    I just don't get it.

    
    #include "c4d.h"
    
    class ValuePair
    {
    private:
    	Int32 mValue1;
    	Int32 mValue2;
    
    public:
    	ValuePair() : mValue1(0), mValue2(0) {}
    	~ValuePair() {}
    };
    
    class ValuePairArray
    {
    	MAXON_DISALLOW_COPY_AND_ASSIGN(ValuePairArray)
    
    private:
    	maxon::BaseArray<ValuePair> mBaseArrayValues;
    
    public:
    	ValuePairArray() {}
    	~ValuePairArray() {}
    
    	// move constructor
    	ValuePairArray(ValuePairArray&& src) :
    		mBaseArrayValues(std::move(src.mBaseArrayValues))
    	{}
    
    	// move assignment operator
    	MAXON_OPERATOR_MOVE_ASSIGNMENT(ValuePairArray);
    
    	// copy
    	MAXON_WARN_UNUSED maxon::Result<void> CopyFrom(const ValuePairArray& src)
    	{
    		if (mBaseArrayValues.CopyFrom(src.mBaseArrayValues) == maxon::FAILED)
    			return maxon::OutOfMemoryError(CREATE);
    		return maxon::OK;
    	}
    };
    
    class StorageWrapper
    {
    	MAXON_DISALLOW_COPY_AND_ASSIGN(StorageWrapper)
    
    public:
    	StorageWrapper() {}
    	~StorageWrapper() {}
    
    	// move constructor
    	StorageWrapper(StorageWrapper&& src) :
    		mBaseArray(std::move(src.mBaseArray)),
    		mHashMap(std::move(src.mHashMap))
    	{}
    
    	// move assignment operator
    	MAXON_OPERATOR_MOVE_ASSIGNMENT(StorageWrapper);
    
    	// copy
    	MAXON_WARN_UNUSED maxon::Result<void> CopyFrom(const StorageWrapper& src)
    	{
    		if (mBaseArray.CopyFrom(src.mBaseArray) == maxon::FAILED)
    			return maxon::OutOfMemoryError(CREATE);
    		if (mHashMap.CopyFrom(src.mHashMap) == maxon::FAILED)
    			return maxon::OutOfMemoryError(CREATE);
    		return maxon::OK;
    	}
    
    	maxon::BaseArray<ValuePairArray> mBaseArray;
    	maxon::HashMap<Int32, ValuePairArray> mHashMap;
    };
    
    void Test()
    {
    	ValuePairArray newitem;
    	Int32 key = 1;
    
    	// using the ValuePairArray class directly
    	maxon::BaseArray<ValuePairArray> arrayOfValuePairArray;
    	maxon::HashMap<Int32, ValuePairArray> mapOfValuePairArray;
    
    	arrayOfValuePairArray.Append(newitem);
    	mapOfValuePairArray.Put(key, newitem);
    
    
    	// using the ValuePairArray class via a storage wrapper class
    	StorageWrapper storageHelper;
    	
    	storageHelper.mBaseArray.Append(newitem);
    	storageHelper.mHashMap.Put(key, newitem);
    }
    
    
    Error	C2248	'ValuePairArray::operator =' : cannot access private member declared in class 'ValuePairArray'
    


  • Hi Daniel, first and foremost I terribly apologize for coming so late here.

    With regard to your question, I've a few comments:

    • maxon::BaseArray can store instance derived by classes that are not necessarily defined using MAXON_DISALLOW_COPY_AND_ASSIGN: the BaseArray Manual present in the Classes section four different cases of classes.
    • in < R20 the maxon::HashMap was not designed to manage instances derived by classes defined with the MAXON_DISALLOW_COPY_AND_ASSIGN: if you want to manage such instances in HashMap in R19 or older it's likely you have to store the instances in the stack and store the references in the HashMap.
      That said, in R19, you can either decide to use a class not defined with MAXON_DISALLOW_COPY_AND_ASSIGN with both maxon::BaseArray and maxon::HashMap or if you want to use the MAXON_DISALLOW_COPY_AND ASSIGN then you've to take care of storing references in the R19 HashMap.
    class TestClassDisallowCopyAndAssign
    {
    	MAXON_DISALLOW_COPY_AND_ASSIGN(TestClassDisallowCopyAndAssign)
    	
    private:
    	Bool _mybool;
    	Int32 _myint;
    	
    public:
    	
    	TestClassDisallowCopyAndAssign() {}
    	TestClassDisallowCopyAndAssign(const Bool boolVal, const Int32 intVal)
    	{
    		_mybool = boolVal;
    		_myint = intVal;
    	}
    	~TestClassDisallowCopyAndAssign() {}
    	
    	// move constructor
    	TestClassDisallowCopyAndAssign(TestClassDisallowCopyAndAssign&& src)
    	{
    		_mybool = std::move(src._mybool);
    		_myint = std::move(src._myint);
    	}
    
    	// move assignment operator
    	MAXON_OPERATOR_MOVE_ASSIGNMENT(TestClassDisallowCopyAndAssign);
    
    	// copy
    	Bool CopyFrom(const TestClassDisallowCopyAndAssign& src)
    	{
    		_mybool = src._mybool;
    		_myint = src._myint;
    		return true;
    	}
    	Bool GetBool() const { return _mybool; }
    	Int32 GetInt32() const { return _myint; }
    };
    
    class TestClass
    {
    	
    private:
    	Bool _mybool;
    	Int32 _myint;
    	
    public:
    	TestClass() {}
    	TestClass(const Bool boolVal, const Int32 intVal)
    	{
    		_mybool = boolVal;
    		_myint = intVal;
    	}
    	~TestClass() {}
    	TestClass(const TestClass& src) : _mybool(src._mybool), _myint(src._myint) { }
    	Bool GetBool() const { return _mybool; }
    	Int32 GetInt32() const { return _myint; }
    };
    
    ...
    	
    	// using BaseArray with a instances that can be copied
    	maxon::BaseArray<TestClass> arrayTestClass;
    	
    	TestClass item1 = TestClass(false, 1);
    	TestClass item2 = TestClass(true, 2);
    	TestClass item3 = TestClass(false, 3);
    	TestClass item4 = TestClass(true, 4);
    	
    	arrayTestClass.Append(item1);
    	arrayTestClass.Append(item2);
    	arrayTestClass.Append(item3);
    	arrayTestClass.Append(item4);
    	
    	for (Int32 i = 0; i < arrayTestClass.GetCount(); i++)
    	{
    		GePrint("arrayTestClass[" + String::IntToString(i) + "]: "+String::IntToString(arrayTestClass[i].GetBool())+"/"+String::IntToString(arrayTestClass[i].GetInt32()));
    	}
    	
    	// using HashMap with a instances that can be copied
    	maxon::HashMap<Int32, TestClass> mapTestClass;
    	
    	mapTestClass.Put(404, item1);
    	mapTestClass.Put(101, item2);
    	mapTestClass.Put(303, item3);
    	mapTestClass.Put(202, item4);
    	for (Int32 i = 0; i < mapTestClass.GetCount(); i++)
    	{
    		const Int32 k = mapTestClass.GetNonEmptyBucket(i)->GetKey();
    		const TestClass value = mapTestClass.GetNonEmptyBucket(i)->GetValue();
    		GePrint("mapTestClass["+String::IntToString(i)+"]: key["+String::IntToString(k)+"], value["+String::IntToString(value.GetBool())+"/"+String::IntToString(value.GetInt32())+"]");
    	}
    	
    	Int32 key = 101;
    	GePrint("mapTestClass@Key[" + String::IntToString(key)+"]: "+ String::IntToString(mapTestClass.FindEntry(key)->GetValue().GetBool()) + "/" + String::IntToString(mapTestClass.FindEntry(key)->GetValue().GetInt32()));
    	
    	key = 303;
    	GePrint("mapTestClass@Key[" + String::IntToString(key)+"]: "+ String::IntToString(mapTestClass.FindEntry(key)->GetValue().GetBool()) + "/" + String::IntToString(mapTestClass.FindEntry(key)->GetValue().GetInt32()));
    	
    	
    	// using BaseArray with instances that can NOT be copied (DISALLOW_COPY_AND_ASSIGN)
    	maxon::BaseArray<TestClassDisallowCopyAndAssign> arrayTestClassDCA;
    	
    	TestClassDisallowCopyAndAssign item1DCA = TestClassDisallowCopyAndAssign(false, 10);
    	TestClassDisallowCopyAndAssign item2DCA = TestClassDisallowCopyAndAssign(true, 20);
    	TestClassDisallowCopyAndAssign item3DCA = TestClassDisallowCopyAndAssign(false, 30);
    	TestClassDisallowCopyAndAssign item4DCA = TestClassDisallowCopyAndAssign(true, 40);
    	
    	arrayTestClassDCA.Append(item1DCA);
    	arrayTestClassDCA.Append(item2DCA);
    	arrayTestClassDCA.Append(item3DCA);
    	arrayTestClassDCA.Append(item4DCA);
    	
    	for (Int32 i = 0; i < arrayTestClassDCA.GetCount(); i++)
    	{
    		GePrint("arrayTestClassDCA[" + String::IntToString(i) + "]: "+String::IntToString(arrayTestClassDCA[i].GetBool())+"/"+String::IntToString(arrayTestClassDCA[i].GetInt32()));
    	}
    	
    	
    	// using HashMap with instances that can NOT be copied (DISALLOW_COPY_AND_ASSIGN)
    	maxon::HashMap<Int32, TestClassDisallowCopyAndAssign*> mapTestClassDCA;
    	mapTestClassDCA.Put(4040, &item1DCA);
    	mapTestClassDCA.Put(1010, &item2DCA);
    	mapTestClassDCA.Put(3030, &item3DCA);
    	mapTestClassDCA.Put(2020, &item4DCA);
    	for (Int32 i = 0; i < mapTestClass.GetCount(); i++)
    	{
    		const Int32 k = mapTestClassDCA.GetNonEmptyBucket(i)->GetKey();
    		TestClassDisallowCopyAndAssign** value = &(mapTestClassDCA.GetNonEmptyBucket(i)->GetValue());
    		GePrint("mapTestClassDCA["+String::IntToString(i)+"]: key["+String::IntToString(k)+"], value["+String::IntToString((*value)->GetBool())+"/"+String::IntToString((*value)->GetInt32())+"]");
    	}
    	
    	key = 2020;
    	GePrint("mapTestClassDCA@Key[" + String::IntToString(key)+"]: "+ String::IntToString(mapTestClassDCA.FindEntry(key)->GetValue()->GetBool()) + "/" + String::IntToString(mapTestClassDCA.FindEntry(key)->GetValue()->GetInt32()));
    	
    	key = 4040;
    	GePrint("mapTestClassDCA@Key[" + String::IntToString(key)+"]: "+ String::IntToString(mapTestClassDCA.FindEntry(key)->GetValue()->GetBool()) + "/" + String::IntToString(mapTestClassDCA.FindEntry(key)->GetValue()->GetInt32()));
    ...
    

    Best, R



  • @r_gigante
    Maybe a little late, but thanks for the info anyway. Always useful, and much appreciated!
    I went with a custom implementation using a BaseArray to store the information, and from there used the specific index in the array as the value in an HashMap

    Instead of the wanted:
    maxon::HashMap<Int32, DataStruct> mymap;

    I went with:

    class MyHashMap
    {
    ...
    maxon::BaseArray<DataStruct> mArray;
    maxon::HashMap<Int32, Int32> mArrayIndexMap;
    };
    

    This is more or less what you have described.

    The only drawback here is that I still haven't found out how to implement range-based loop support ... but I went with using the old-fashioned indexed loop usage. Good enough for its purpose.