C++ mixer splitter implementation without ATL

C++ mixer splitter implementation without ATL

Postby greTol » 13 May 2015, 14:55

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.
greTol
 
Posts: 20
Joined: 26 August 2013, 14:45

Re: C++ mixer splitter implementation without ATL

Postby jasper » 13 May 2015, 19:19

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.
User avatar
jasper
 
Posts: 1128
Joined: 24 October 2012, 15:33
Location: Spain

Re: C++ mixer splitter implementation without ATL

Postby greTol » 02 July 2015, 08:53

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.
greTol
 
Posts: 20
Joined: 26 August 2013, 14:45

Re: C++ mixer splitter implementation without ATL

Postby jasper » 02 July 2015, 11:46

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.
User avatar
jasper
 
Posts: 1128
Joined: 24 October 2012, 15:33
Location: Spain

Re: C++ mixer splitter implementation without ATL

Postby greTol » 10 July 2015, 14:38

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?
greTol
 
Posts: 20
Joined: 26 August 2013, 14:45

Re: C++ mixer splitter implementation without ATL

Postby jasper » 10 July 2015, 15:44

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;
User avatar
jasper
 
Posts: 1128
Joined: 24 October 2012, 15:33
Location: Spain


Return to Unit Operations

Who is online

Users browsing this forum: No registered users and 1 guest

cron