Cannot access private member declared in class



  • On 03/03/2014 at 15:28, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   13,14,15 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    You experienced C++ guys, who do I get:

    error C2248: 'BaseList2D::BaseList2D' : cannot access private member declared in class 'BaseList2D'

    when I try to compile this code:

    	class ButterFly : public BaseList2D
    	{
    	  private:
    		ButterFly();
    		~ButterFly();
    	  public:
    	};
    

    The Cinema 4D SDK is chock full of classes, derived from the BaseList2D, declaring the class exactly this way. So why do I get this error?



  • On 03/03/2014 at 22:48, xxxxxxxx wrote:

    You can not create an instance of a class that has a private constructor. There is only
    one situation in which it makes sense to create your own BaseList2D subclass. Imagine
    you have your own NodeData subclass that implements some new features (such as TagData
    and ObjectData do) and you want to provide an API for other people to access internal data
    from this NodeData plugin.

    template<typename Type> inline Type* RetrieveTableX(const GeListNode* obj) {
        NodeData* data = obj->GetNodeData();
        return (Type* ) C4DOS.Bl->RetrieveTableX((NodeData* ) data, 0);
    }
      
    class ButterFly;
      
    class ButterFlyData : public NodeData {
      
    public:
      
        /* Called when ButterFly::SetSomeValue() is called. */
        virtual void SetSomeValue(Float value) {
            // ...
        }
      
    };
      
    struct BUTTERFLYPLUGIN : public NODEPLUGIN {
        void (ButterFlyData::*SetSomeValue)(Float);
    };
      
    class ButterFly : public BaseList2D {
    private:
        ButterFly();
        ~ButterFly();
    public:
      
        void SetSomeValue(Float value) const {  
            ButterFlyData* data = static_cast<ButterFlyData*>(GetNodeData());
            if (!data) return nullptr;
      
            BUTTERFLYPLUGIN* method_table = RetrieveTableX<BUTTERFLYPLUGIN*>(data);
            if (!method_table) return nullptr;
      
            return (data->*method_table->SetSomeValue)(this, value);
        }
      
        static ButterFly* Alloc() {
            return static_cast<ButterFly*>(BaseList2D::Alloc(ID_BUTTERFLY_PLUGIN));
        }
      
        static void Free(ButterFly*& ptr) {
            #ifdef DEBUG
                if (!ptr) {
                    DiagnosticOutput("Passed nullptr to ButterFly::Free()");
                    DebugStop();
                }
                if (!ptr->IsInstanceOf(ID_BUTTERFLY_PLUGIN)) {
                    DiagnosticOutput("Non-Butterfly Plugin node passed to ButterFly::Free()");
                    DebugStop();
                }
            #endif
            if (ptr) {
                BaseList2D::Free(ptr);
            }
        }
      
    };
    

    This is very very advanced stuff though.

    Best,
    -Niklas



  • On 04/03/2014 at 02:49, xxxxxxxx wrote:

    Hi Niklas!
    I try to learn, and your help is invaluable!
    In this case, I would learn much more, if you could take your time to correct the errors I get. 
    I replaced Float by Real, because I got errors (R14 <--> R15)? But that should not influence the code otherwise.

    1) This error is obvious, right? Just remove nullptr.

    2) Here I have no clue

    3) Seems that the error goes away when I remove "this"

    4) If I replace ButterFly* with  BaseList2D*, the error goes away, but I am not sure if that correction is right?

    Ok, any help on this is very welcome, because this is what I learn from!



  • On 04/03/2014 at 05:07, xxxxxxxx wrote:

    Hi ingvar,

    I'm sorry that I forgot to add that I wrote the example completely from scratch in the plugincafe
    reply editor. I'll give you a corrected version soon, just need to find the time for it.

    I still have a Jetlag, so sorry for the issues. I actually expected it to have more errors.. 

    1. Yes, I originally intended the function to return a pointer and forgot to fix the return statements.
    2. Just grabbed the code from an existing plugin of mine but I didn't see that I actually made a
      second overload for it. Make it
    template<typename Type> inline Type* RetrieveTableX(NodeData* data) {
        return (Type* ) C4DOS.Bl->RetrieveTableX(data, 0);
    }
    
    1. Either remove the this or ass ButterFly* as the first parameter to the SetSomeValue() method.

    2. No, that would lead to an infinite recursive call. I forgot to cas to a BaseList2D reference.

    BaseList2D::Free((BaseList2D*&) ptr)
    

    I won't guarantee that it compiles fine now, though. I think best might be if I give you
    a working example.

    But let me ask you first: Why do you actually want to create a BaseList2D subclass? Any
    special reason for this? As I said above already, it usually makes sense only when you
    want to provide an API to your plugin for other developers.

    Best,
    -Niklas



  • On 04/03/2014 at 06:58, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    I won't guarantee that it compiles fine now, though. I think best might be if I give you
    a working example.

    Hi Niklas!
    I got it working!
    Here is code that compiles:

    template<typename Type> inline Type* RetrieveTableX(NodeData* data)
    {
    return (Type* ) C4DOS.Bl->RetrieveTableX(data, 0);
    }
    class ButterFly;
    class ButterFlyData : public NodeData
    {
    private:
    Real _someValue;
     public:
     
    /* Called when ButterFly::SetSomeValue() is called. */
    virtual void SetSomeValue(Real value)
    {
    _someValue = value;
    }
    virtual Real GetSomeValue()
    {
    return _someValue;
    }
      
    static ButterFlyData* NewButterFlyData()
    {
    return gNew ButterFlyData;
    }
    };
    struct BUTTERFLYPLUGIN : public NODEPLUGIN
    {
    void (ButterFlyData::*SetSomeValue)(Real);
    Real (ButterFlyData::*GetSomeValue)();
    };
    class ButterFly : public BaseList2D
    {
     private:
    ButterFly();
    ~ButterFly();
     public:
     
    void SetSomeValue(Real value) const
    {
    ButterFlyData* data = static_cast<ButterFlyData*>(GetNodeData());
    if(!data) 
    return;// nullptr;
    BUTTERFLYPLUGIN* method_table = RetrieveTableX<BUTTERFLYPLUGIN>(data);
    if(!method_table) 
    return;// nullptr;
    return (data->*method_table->SetSomeValue)(value);
    }
    Real GetSomeValue() const
    {
    ButterFlyData* data = static_cast<ButterFlyData*>(GetNodeData());
    if(!data) 
    return -9.9;// nullptr;
    BUTTERFLYPLUGIN* method_table = RetrieveTableX<BUTTERFLYPLUGIN>(data);
    if(!method_table) 
    return -9.9;// nullptr;
    return (data->*method_table->GetSomeValue)();
    }
      
    static ButterFly* Alloc()
    {
    return static_cast<ButterFly*>(BaseList2D::Alloc(ID_BUTTERFLY_PLUGIN));
    }
    static void Free(ButterFly* &ptr)
    {
    #ifdef DEBUG
    if(!ptr)
    {
    DiagnosticOutput("Passed nullptr to ButterFly::Free()");
    DebugStop();
    }
    if(!ptr->IsInstanceOf(ID_BUTTERFLY_PLUGIN))
    {
    DiagnosticOutput("Non-Butterfly Plugin node passed to ButterFly::Free()");
    DebugStop();
    }
    #endif
    if(ptr)
    {
    BaseList2D::Free((BaseList2D*&)ptr);
    }
    }
    };
    

    But I have this issue now, I cannot Register it:

    1. Bool b = RegisterNodePlugin(ID_BUTTERFLY_PLUGIN, "butterfly", PLUGINFLAG_HIDE
    2. , (NodeData* )ButterFlyData::NewButterFlyData
    3. , NULL, 0, 0);
    

    Line 2, have no idea how to write a DataAlloctator that will satisfy the compiler.
    So I am stuck here. Can you help me out here? When this compiles, I think I have it!

    Originally posted by xxxxxxxx

    But let me ask you first: Why do you actually want to create a BaseList2D subclass? Any
    special reason for this? As I said above already, it usually makes sense only when you
    want to provide an API to your plugin for other developers.

    Can we make a deal? When it compiles, and registers properly, so that I can use it, I will tell you. Ok?



  • On 04/03/2014 at 08:34, xxxxxxxx wrote:

    Oh my gosh, this has never happened to me before. I accidentally edited your post instead of
    posting my own, ingvar! I'm so sorry! Very emberassing..

    Edit: Phew! your post was still in my browser history!

    > Can we make a deal?

    All right! Pass ButterflyData::NewButterflyData() as the Data Allocator. if that doesn't work
    right away, create the same function again but make it return NodeData* instead.



  • On 04/03/2014 at 09:34, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Oh my gosh, this has never happened to me before. I accidentally edited your post instead of
    posting my own, ingvar! I'm so sorry! Very emberassing.. 
    Edit: Phew! your post was still in my browser history!

    No problem, I have almost no idea about what you are talking about, almost like reading the C4D SDK..  
    You are a moderator, so your duty is to edit other's posts, isn't it? Big smile

    Originally posted by xxxxxxxx

    All right! Pass ButterflyData::NewButterflyData() as the Data Allocator. if that doesn't work
    right away, create the same function again but make it return NodeData* instead.

    Yes, it did not work as you said, and making it return NodeData* works, as you said, but introduces another problem:

    	void SetSomeValue(Real value) const
    	{
    		ButterFlyData* data = static_cast<ButterFlyData*>(GetNodeData());
    		if(!data) 
    			return;// nullptr;
    		
    		BUTTERFLYPLUGIN* method_table = RetrieveTableX<BUTTERFLYPLUGIN>(data);
    		if(!method_table) 
    			return;// nullptr;
    			
    		 **// Will crash here** 		
    		return (data->*method_table->SetSomeValue)(value);
    	}
    

    It will crash, probably because it "believes" it is a pure NodeData, the ancestor, and not the descendant (?).
    I am almost giving up on this one, but regardless of my intentions with this, I learn a lot, and it would be really interesting to make it work, still.


Log in to reply