Stuck, can't make an Interface extend ReferenceCountedObject


#1

I’m pretty sure subClassing would defeat the “An interface looks like a class, but has no implementation.” thing, but I’m not sure how else to do this?

I want to use an interface to describe generic behavior for my different Topology classes, and then be able to pass the concrete implementations into another class as the interface type…

So ISOMLatticeTopology.h is implemented by CSOM2DLatticeTopology, CSOM3DLatticeTopology, and CSOM3DLatticeTopology. At run time, I want to be able to pick which Topology I would like, and then the concrete class is passed into my CSOMLatticeSettings class as a type ISOMLatticeTopology (see below).

I tried to thin the code out here just to show one example, but its happening a ton as I’m trying to pass around a bunch of my classes this way. Is there a better way to pass classes? I can do it without making them reference counted. Am I misunderstanding some fundamental thing about reference counting objects?

Additionally, I would like the Objects to be const. However, I keep getting things like /Developer/juce/juce_amalgamated.h:6460: error: passing ‘const ISOMLatticeTopology’ as ‘this’ argument of ‘void juce::ReferenceCountedObject::decReferenceCount()’ discards qualifiers

CSOMMain.h

class CSOMMain
{
public:
    //==============================================================================
	CSOMMain();
	~CSOMMain();

	void initSOM();
	
private:
	ReferenceCountedObjectPtr<const ISOMLatticeTopology> m_pSOMTopology;
	ReferenceCountedObjectPtr<const CSOMLatticeSettings> m_pLatticeSettings;
			
};

CSOMMain.cpp

void CSOMMain::initSOM()
{
	
	//create topology
	m_pSOMTopology = new CSOM2DLatticeTopology(10,10);
	
	//create the settings
	m_pLatticeSettings = new CSOMLatticeSettings();	//make a settings object
	m_pLatticeSettings->LatticeTopology( m_pSOMTopology ); //set the topology obj.	
}

Here is the interface where I want to subclass ReferenceCountedObject, but I get the error: expected class-name before ‘{’ token

ISOMLatticeTopology.h

class ISOMLatticeTopology  :  public ReferenceCountedObject
{
public:

	ISOMLatticeTopology() {};

	virtual double LatticeDistance(long CellIndex1, long CellIndex2) const =0;
		//	returns a value which represents the topological distance
		//	between the cells corresponding to passed indicies
		//	It is the user's responsibility to define the representation

	virtual unsigned long NumberOfCells() const =0;
		//	returns the number of cells necessary for the given 
		//	topology.

	virtual ~ISOMLatticeTopology() {};
};

Thanks for any help,

Owen


#2

So, we do have a terminology mismatch, but let me have a go. You’’ know most of this.

Juce is one of the few C++ systems that actually embraces multiple inheritance. It uses the C++ equivalent to ‘interfaces’, generally called abstract base classes (ABCs). That means that you can use multiple ABCs in your classes, and they pick up the abilities.

Making base classes very abstract helps avoid amibiguity - no concrete methods means less chance of duplicate inheritance.

There is still a lot of info about encapsulation versus ownership - you seem to be making these decisions. The best approach to deciding is to look at what Jules has done, to be honest. You can’t really pick your own approach 100% when using a framework.

Ok, to the specifics. Your approach looks fine, to me.

‘Here is the interface where I want to subclass ReferenceCountedObject, but I get the error: expected class-name before ‘{’ token’

That really just sounds like either a syntax error - like did the class defined just before close it’s definition> (missing : would do this), or an include error - if ISOMLatticeTopology.h doesn’t include the juce headers, it won’t know what ReferenceCountedObject is.

error: passing ‘const ISOMLatticeTopology’ as ‘this’ argument of ‘void juce::ReferenceCountedObject::decReferenceCount()’ discards qualifiers

This can happen when things have been locked down, and even de-referencing a pointer (to get to ‘this’, as required to call a method) can break const. You may need a const_cast, but you also may have mis-defined. Surely you want a const instance (object), not a const class. What’s the code that made it const? Was it:

const ISOMLatticeTopology iso (blah, blah);

In which case - that’s odd - if it’s a ReferenceCounted object, then using it like that would be super weird, and

const ReferenceCountedObjectPtr etc.

But then - you may be defining the refptr as const, not the object. Dunno. I hate const definitions.

Bruce


#3

Thanks for the info. It turned out I forgot to add the JUCE header to the Abstract Base Class ISOMLatticeTopology ( I think I have the terms right now). That cleared up the Syntax error; however, I still get the error regarding the Const.

I have a theory on why I’m getting that error though. I want to pass referenceCountedObjectPtrs into other classes, but keep the objects they point at from being modified ( I have a dataSet Class that I want to be read-only for most classes once its instatiated). With normal naked pointers, I just make sure the object is passed in as Const and then I know the object is safe from being modified. With referenceCountedObjectPtrs, the objects being pointed at have to come from a subclass of referenceCountedObject. referenceCountedObject needs to be able to modify the current count of referenceCountedObjectPtrs. If I try to pass in an object as Const, and that object subClasses referenceCountedObject, then that means I wouldn’t be able to change the current number of referenceCountedObjectPtrs… Sorry if I’m mixing terms again, I hope that makes sense? :slight_smile:

Basically, how do I keep track of my pointers for memory leaks, pass the object it points to between classes, and have the ability to make it Const?

I’ve tried ScopedPointers, and referenceCountedObjectPtrs… I know there are a few others, but maybe I’m using the wrong ones for this stuff?

For now, I’ve removed the Consts where they were throwing errors… But I’d like to protect the objects from change if I could.


#4

It’s a good question…

It doesn’t make much sense to have a ReferenceCountedObjectPtr , because for the pointer object to do its job, it must be able to increment/decrement the ref count on the object, and to delete it, and those methods are definitely non-const.

There are a couple of approaches to making a const version of the class though. I could add a second template parameter, which would be the type that should be returned when it’s dereferenced - normally that’d default to the normal type, but you could optionally make it a const version instead.

Or, there could be a new class ReferenceCountedConstObjectPtr, which would be mostly the same as the normal one (i.e. you could only create it from a non-const raw pointer), but which returns a const Type when you dereference it.

Interesting c++ stuff anyway, I’ll have a ponder…


#5

Awesome. Thinking about all that, my use of the ReferenceCountedObject might not even be necessary… But then again… I’m new to a lot of this and still very confused.

Basically, I think it boils down to whether I should create my DataSet class ( or any other class I want to pass a pointer/references of into other classes ) on the Stack or the Heap? If I make it on the stack, then can I pass it around as Const into other classes using pointers/references without worrying about memory leaks? Since I don’t need to change any members in the class, would this make the most sense?

Otherwise, if I make it on the Heap don’t I make the Stack smaller (faster?), but then also I would want to use the ReferenceCountedObject/ReferenceCountedObjectPtr to prevent memory leaks?

I’ve been looking through your Juce Demo code and Jucer(experimental) code, but I can’t discern when its appropriate to do it one way or the other? Also didn’t see any ReferenceCountedObjectPtr in any of the Demo or Jucer code, so not sure if I should be using them?

Thanks for any advice,
Owen


#6

Another very neat approach can be to use a wrapped reference-counted object, which is what I did with ValueTree - you use ValueTree objects as copy-by-value, but internally they have a ref-counted pointer to a shared heap object. Doing it that way can make the class very clean to use, at the expense of having to write a lot of boiler-plate code to redirect the methods.


#7

Cool, So I might convert my DataSet Class (which currently loads the feature data using XML) into using a ValueTree Object. I have another question about ScopedPointers though…

If I use a ScopedPointer to create and pass in an object from the Shared Heap like this:

Main.h

    //==============================================================================
    void initialise (const String& commandLine)
    {
        // Do your application's initialisation code here..
        mainWindow = new MainAppWindow();
		
		//Create XML file Object
		J_TempXMLWriter tempForTesting;
		
		//get the dataset going
		SOMData = new DataSet();
		
                //load and parse XML
		SOMData->LoadDataSet("../DataSet.xml");
		
                //pass SOMData ScopedPointer intoSOMMain class
		CSOMMain SOMMain( SOMData );
		
		SOMMain.initSOM();		
    }

private:
	ScopedPointer <DataSet> SOMData;

What is the best way to pass the object? Should I

a) pass the ScopePointer Object in like this “CSOMMain SOMMain( SOMData );” and have CSOMMain’s Constructor look like this "CSOMMain(const DataSet *pDataSet)"
b) pass the ScopePointer Object in like this “CSOMMain SOMMain( *SOMData );” and have CSOMMain’s Constructor look like this "CSOMMain(const DataSet & pDataSet)"
c) ??? some other way???

What I’m unclear about is, what happens is I pass in the pointer/reference and some other class later on calls deleteAndZero(pDataSet)? Or should I make all the pointers after the first ScopedPointer, ScopedPointers as well? What happens if one goes out of Scope and ScopedPointers higher up then try to delete the Object? Should I only make the class that created the initial ScopedPointer worried about deleting it… what happens if the class that created the ScopedPointer gets deleted, but the DataSet class should live on?

Sorry if thats a lot of questions at once. Just trying to wrap my head around object ownership, pointer, and memory stuff. Seems like one of those amazing flexible ideas, that are hard to learn as the choices are all context based. Just looking for some good rules of thumb to get started.


#8

Sounds like you’re asking “how should I handle object ownership in c++”, which is a bit of a big question! In a nutshell, I’d say:

  • Use stack and embedded member objects, and copy-by-value whenever possible
  • If you can use a reference instead of a pointer, always do so
  • If you really do need to create a heap object and have a pointer to it, always use a ScopedPointer/ReferenceCountedPointer/some other RAII-based class. Use ‘delete’ only if there’s absolutely no alternative.

#9

Thanks for the great “pointers” :slight_smile: But seriously, all this pointer stuff is starting to make a lot more sense, as is the layout of building a JUCE app. I used the JUCER to make my latest project, and it created three files for me.

Main->MainWindow->ContentComponent

My question now is I noticed in your JUCER(Experimental) code that in the jucer_application.h you call a setProject(project*) method in the MainWindow which call a setProject(project*) in the ContentComponent. This requires you to add the project.h file to both of those classes, and hard links your GUI component and DocumentWindow to the Data Model… or I could be misunderstanding. I’m still learning about patterns, when to implement them, and when to not “over design”. Is this cool to do? Is the the way to link your Model to your View?

Eventually I want to have several different ways to process/analyze a single set of data, and several different ways to visualize that same data. Should I separate the Data Models from the GUI components more using a controller/mediator class? Or should I just hard set these pointers like in the JUCER code?

Thanks again for your advice, and thoughts.


#10

Sorry Duplicate post… deleted it see above.