Page 1 of 1

C++ mixer splitter implementation without ATL

Posted: 13 May 2015, 14:55
by greTol
Hi there,

I'm still struggling with rebuilding the mixer splitter example without ATL.
Thanks to the "AmsterCHEM COM CAPE-OPEN Wizard" I already got a frame for the unit operation. I also added a "Collection" class (.h and .cpp) implementing ICapeCollection. But looking in the CO-LaN example, a function "CCollection::CreateCollection()" is defined and implemented, that creates an ATL::CComObject instance:

Code: Select all

static CComObject<CCollection> *CreateCollection(const OLECHAR *name,const OLECHAR *description)
    {CComObject<CCollection> *p;
     CComObject<CCollection>::CreateInstance(&p); //create the instance with zero references
     p->AddRef(); //now it has one reference, the caller must Release this object
     p->name=name;
     p->description=description;
     return p;
    }
My question is:
How should I replace this function in an implementation without the ATL stuff?
The function is so far called in the constructor of the unit operation, before adding things to the collection:

Code: Select all

     //create port collection
     portCollection = CCollection::CreateCollection(L"Port collection", L"Port collection for CPP Mixer Splitter");
     //add port
     portCollection->AddItem(port);
I'm still looking for any full example of a cape-open-compliant unit operation in c++ that does not depend on commercial versions of MSVC. If someone provided me such an example project, I would be very glad.

Thanks in advance for any helpful replies.

Re: C++ mixer splitter implementation without ATL

Posted: 13 May 2015, 19:19
by jasper
If your collection class has a constructor without arguments, you can simply create is as a class variable. I imagine that you have derived it from COMObject, or CAPEOPENBASE (which derives from COMObject), and then the reference count will be 1, initially. E.g.

Code: Select all

class MyPortCollection : public CAPEOPENBASE {

 public:

   MyPortCollection() {
     ...
   }

  ...

};
Then in the unit operation, you can do

Code: Select all

class aUnitOperation : public .... {

  MyPortCollection portCollection;

  ...

};
Future versions of the framework will create COM classes with an initial reference count of zero. In this case, if you want to use your collection class as static member, you may call AddRef in its constructor, or after instantiation, to prevent it from being deleted.

Re: C++ mixer splitter implementation without ATL

Posted: 02 July 2015, 08:53
by greTol
I once again spend some time trying to rebuild the MixerSplitterExample. In the collection class the example code is not working anymore with the new base class because of the change from 'public' to 'private' of the CAPEOPENBASE member field "name".
Old example code (Collection.h):

Code: Select all

STDMETHOD(Item)(VARIANT id, LPDISPATCH * Item){   
	    int index=-1; //index of the item in the collection, if we find it
	    //is the value a name? we presume so in case it is a string
	    if (id.vt==VT_BSTR)
	     {//string
	      for (index=0;index<(int)items.size();index++)
	       {if (CBSTR::Same(id.bstrVal,items[index]->name.c_str()))
	         break;
	       }
	     }
...
}
My new approach based on the framework (Collection.cpp):

Code: Select all

STDMETHODIMP Collection::Item(/*[in]*/ VARIANT id, /*[out, retval]*/ LPDISPATCH *itemResult) {
	int index = -1; //index of the item in the collection, if we find it
	//is the value a name? we presume so in case it is a string
	if (id.vt == VT_BSTR)
	{//string
		for (index = 0; index<(int)items.size(); index++)
		{
			if (SameString(id.bstrVal, items[index]->name.c_str()))
				break;
		}
	}
...
}
The 'if'-condition in the 'for'-loop obviously cannot access the "name" anymore, since it has now private access in CAPEOPENBASE.
There seems to be a getter function for the "name" in the CAPEOPENBASE, but I'm not sure if and how to use it:

Code: Select all

STDMETHOD(get_ComponentName)(/* [retval][out] */ BSTR *name) {
		if (!name) {
			return E_POINTER;
		}
		*name=SysAllocString(this->name.c_str());
		return NO_ERROR;
}
Jasper, what is your sugggestion on how to replace the if-condition and how to access the CAPEOPENBASE's "name" attribute in a proper way?
I guess the solution is kind of a "three liner", but I don't get it, yet.

Thanks in advance for any helpful reply.

Re: C++ mixer splitter implementation without ATL

Posted: 02 July 2015, 11:46
by jasper
You can simply make the name field of the base class public. Or you can implement a public member

const wchar_t *Name() {
return name.c_str();
}

in the base class, which is a "nicer" thing to do, or this

const wstring &getName() const {
return name;
}

(I believe that one is already there, you can use it directly).

But, you may want to make a new project, and generate a unit operation in there. This will ask you to put down the entire implementation of the unit operation. Have a look at the collection class that is generated there. You could copy this class into your existing project.

This collection keeps both a vector of the objects and a hash map of the indexes of the objects by name. Considerably more efficient in case there are more than a few items in the collection.

Re: C++ mixer splitter implementation without ATL

Posted: 10 July 2015, 14:38
by greTol
Some basic question regarding the COMSmartPtr and the access of collection elements:

//This is given (created by the COM CO Wizard 2.0)
Collection<Parameter,true> parameterCollection;

//Now I want to access elements of this collection:
//This gives no error, but I need a RealParameter
COMSmartPtr<Parameter> param = parameterCollection.items[0];

//This would be nice, but erroneous ("base class IUnknown ambiguous(?)")
COMSmartPtr<RealParameter> param = parameterCollection.items[0];

//Not working either, same error
COMSmartPtr<RealParameter> param = (COMSmartPtr<RealParameter>)parameterCollection.items[0];

How can I correctly access the elements in that collection, getting the exact type etc?

Re: C++ mixer splitter implementation without ATL

Posted: 10 July 2015, 15:44
by jasper
cast to Parameter*, cast to RealParameter*:

Code: Select all

COMSmartPtr<RealParameter> param = (RealParameter*)(Parameter*)parameterCollection.items[0];
But for a manageable number of parameters, this is handier:

In class definition:

Code: Select all

COMSmartPtr<RealParameter> testRealParam; //should be declared in class
in constructor

Code: Select all

testRealParam=new RealParameter(L"TestReal",L"Test Real Parameter",CAPE_INPUT,0.2,0,1.5,CreateDimension().Set(Meter,1).Set(Second,-2),parameterChanged);
parameterCollection.Add(testRealParam);
on use:

Code: Select all

double a=testRealParam->value;