BaseContainer equality ... are you kidding me



  • I was debugging a plugin I am working on to find an issue ...
    After finding the actual cause of the problem, I just couldn't believe this.
    So, I made a test CommandData plugin with only the problematic code as part of the Execute

    Bool MyCommand::Execute(BaseDocument* doc)
    {
    	BaseContainer bc1;
    
    	bc1.SetInt32(1, 1234);
    	bc1.SetString(2, "abcd"_s);
    	bc1.SetFloat(3, 0.1234);
    	bc1.SetBool(4, true);
    
    	BaseContainer bc2;
    
    	bc2.SetString(2, "abcd"_s);
    	bc2.SetFloat(3, 0.1234);
    	bc2.SetBool(4, true);
    	bc2.SetInt32(1, 1234);	// this line was moved from being the first to being the last
    
    	ApplicationOutput("BaseContainer 1 equals BaseContainer 2: @", bc1 == bc2);
    	ApplicationOutput("BaseContainer 1 not equals BaseContainer 2: @", bc1 != bc2);
    
    	return true;
    }
    

    Notice that the second BaseContainer is just a duplicate of the first one. The only thing which differs, is that I moved the first line of data assignment to be the last one.
    In other works: the IDs and values are E-X-A-C-T-L-Y identical, only the order/sequence in which they are applied is different.

    According to the documentation:

    Bool operator== 	( 	const BaseContainer &  	d	) 	const
    Equality operator. Checks if the containers have the same IDs, the same values and all values are equal.
    
    Parameters
        [in]	d	The container to compare against.
    
    Returns
        true if the containers have the same IDs, the same values and all values are equal, otherwise false. 
    
    Bool operator!= 	( 	const BaseContainer &  	d	) 	const
    Not equal operator. Checks if the containers have different IDs, different values or values are different.
    
    Parameters
        [in]	d	The container to compare against.
    
    Returns
        true if the containers have different IDs, different values or values are different, otherwise false. 
    

    Same IDs, same values. You would thus expect the two to be equal.
    And yet, when I execute the plugin, following is the result shown in the console:

    BaseContainer 1 equals BaseContainer 2: false
    BaseContainer 1 not equals BaseContainer 2: true
    

    As this is just a fundamental check, and it provides me a result which is opposite to what is to be expected (from logic and from what documentation says) ... I must be doing something wrong here, right?
    So, what am I overlooking this time?



  • Hello @C4DS
    Reading from the documentation

    "Two BaseContainer are identical if they have the same ID, the same number of entries and if the entries are also identical"

    I guess that unless you have the entries identical in both BaseContainers which in your case they are not, they can not be equal.
    Why don't you try sorting the entries first using BaseContainer::Sort() function and then comparing them. This would do your work.

    Bool MyCommand::Execute(BaseDocument* doc)
    {
        BaseContainer bc1;
    
        bc1.SetInt32(1, 1234);
        bc1.SetString(2, "abcd"_s);
        bc1.SetFloat(3, 0.1234);
        bc1.SetBool(4, true);
    
        BaseContainer bc2;
    
        bc2.SetString(2, "abcd"_s);
        bc2.SetFloat(3, 0.1234);
        bc2.SetBool(4, true);
        bc2.SetInt32(1, 1234); // this line was moved from being the first to being the last
    
        bc1.Sort();
        bc2.Sort();
    
        ApplicationOutput("BaseContainer 1 equals BaseContainer 2: @", bc1 == bc2);
        ApplicationOutput("BaseContainer 1 not equals BaseContainer 2: @", bc1 != bc2);
    
        return true;
    }
    

    I hope this could help you.



  • @Ogers
    Thanks for your answer.
    But our interpretation of the documentation differs.

    => In both cases did I use the same IDs, in both cases did I use the same values ... and obviously both have the same number of entries.
    To me (and from my interpretation of the documentation) this would mean they're identical. Documentation does not mention about sorting to make them identical.
    Ot only says: same IDs + same values -> identical

    If your interpretation of the documentation is how it should be then I suggest the documentation to be clarified.

    EDIT:
    I noticed you referred to the "BaseContainer manual" documentation, while I referred to the actual function documentation from the SDK.
    There seems to be a "slight" difference:

    Function doc:
    Equality operator. Checks if the containers have the same IDs, the same values and all values are equal.

    Manual:
    Two BaseContainer are identical if they have the same ID, the same number of entries and if the entries are also identical

    From the manual it would imply that the basecontainers need to have a same ID (I interpret this as the basecontainer's ID), and further talks about "entries".
    The function doc, mentions the IDs should be the same (meaning the "entries" should have the same IDs). No talk about sorting nor "entries".

    Confusing!



  • hello,

    We can confirm this. Sorting doesn't help because Sort() sort the IDs removing the value except String and SubContainer.

    As you may know BaseContainer are heavily used in Cinema4d so i doubt that the Operator will be changed but wait and see.
    Once the decision is made, we will update the documentation accordingly.
    Thanks to point us to this, i'll get back with any information that i'll receive.

    So if you want to compare BaseContainer you have to come with your own solution to "really" compare them.

    Cheers
    Manuel



  • hello,

    While your suggestion is logical, but due to its implementation, BaseContainer should have to be either sorted when a new ID is inserted or when we use the == operator. Because Cinema4D's make an intensive use of BaseContainer, doing so would likely have a performance issue.

    I've added a note in the documentation and in the manual to be a bit more clear about this. (will be available for the next release of the documentation)

    Cheers
    Manuel



  • @m_magalhaes said in BaseContainer equality ... are you kidding me:

    ... BaseContainer should have to be either sorted when a new ID is inserted or when we use the == operator.

    I understand this sorting is not part of the current implementation, due to performance issue.
    As such, I wouldn't mind having to perform a sorting on my own BaseContainer ... but as you mentioned in an earlier post, the sorting would actually destroy the data (except for strings).

    So, how should one use BaseContainer when items are inserted dynamically (e.g. not knowing which ID comes first), AND being able to perform a comparison?

    Just for completeness of background info, this post is also related to
    https://plugincafe.maxon.net/topic/11581/basecontainer-and-range-based-loop



  • hello,

    while we are still looking at the other post, regarding BaseContainer equality, something like this maybe. A recursive function (in case of a sub container).

    static Bool BaseContainerCompare(const BaseContainer& bc1, const BaseContainer& bc2)
    {
    
    	maxon::Int32	 id, id2;
    	GeData *gbc1, *gbc2;
    	BrowseContainer browsebc1(&bc1);
    	BrowseContainer browsebc2(&bc2);
    	if (bc1.GetId() != bc2.GetId()) 
    		return false;
    
    	while (browsebc1.GetNext(&id, &gbc1))
    	{
    		// Be sure there are still elements in second container or quit as fast as possible
    		if (!browsebc2.GetNext(&id2, &gbc2))
    			return false;
    
    		// Retrieves the data on the second BaseContainer
    		const GeData data = bc2.GetData(id);
    
    		// if it's a sub-container we can't rely on == or != operator
    		if (gbc1->GetType() == DA_CONTAINER && data.GetType() == DA_CONTAINER)
    		{
    			if(BaseContainerCompare(*data.GetContainer(), *gbc1->GetContainer()))
    				continue;
    			else
    				return false;
    		}
    		
    		// Compares both value as they are not BaseContainer
    		if (*gbc1 != data)
    		{
    			return false;
    		}
    	}
    
    	// if there are still element in the second BaseContainer return false.
    	if (browsebc2.GetNext(&id2, &gbc2))
    		return false;
    
    	return true;
    }
    

    cheers
    Manuel



  • hello,

    it's been 14 days without further feedback, i'll consider this thread as "solved" tomorrow.

    Cheers
    Manuel



  • Only thing I have to add is that when trying out the provided example code I removed the double const in the static function declaration.

    From:

    static Bool BaseContainerCompare(const BaseContainer& const bc1, const BaseContainer& const bc2)
    

    To:

    static Bool BaseContainerCompare(const BaseContainer& bc1, const BaseContainer& bc2)
    

    as the original line resulted in a:

    warning C4227: anachronism used: qualifiers on reference are ignored
    


  • hello,

    oupss thanks i got the same warning and forgot to update the code here

    Cheers
    Manuel


Log in to reply