Archived

This forum has been archived. Please start a new discussion on GitHub.

Efficient replacement for huge CORBA union.

Hello.

I've heard about ICE a few days ago in slashdot pointed article "The Rise and Fall of Corba" by Michi Henning. As ICE seems to be a very good replacement for difficult to use CORBA I'm trying to translate my IDLs to Slice and learn how to deal in this new environment.

As far the most difficult case is to find good solution for CORBA union replacement. I've read docs and forum threads about this but I'm not sure if proposed solution is the best in my case.

In CORBA I've created a set of generic functions to create, modify and delete some objects. Please look at the code snippet.

1. I created structures for over 30 objects:
struct tOperator {
  string code;
  string name;
};

struct tService {
  string name;
};

struct tPrice {
  price_t price;
  date_t start_date;
  date_t end_date;
};

...


2. I've created object type enumerator for use in union container.
enum object_type_t {
  OT_OPERATOR,
  OT_SERVICE,
  OT_PRICE,
  ...
};

union tObject switch (object_type_t) {
  case OT_OPERATOR:
    tOperator m_operator;
  case OT_SERVICE:
    tService m_service;
  case OT_PRICE:
    tService m_price;
  ...
};

3. The functions:
interface ObjectManipulator {
  bool createObject(out obj_id_t objid, in tObject obj);
  bool modifyObject(in obj_id_t objid, in tObject obj);
  bool deleteObject(in obj_id_t objid, in object_type_t objtype);
};


In a servant I can easily and fast work on passed objects, ie:
bool createObject(obj_id_t objid, tObject& obj)
{
  switch (obj._d()) {
    case OT_OPERATOR:
      createOperator(obj.m_operator());
      break;
    case OT_SERVICE:
      createService(obj.m_service());
      break;
    case OT_PRICE:
      createPrice(obj.m_price());
      break;
    ...
  }
}

This approach let me to create simple, easy to maintain, IDL API.

The problem is how to translate it to Slice and ICE. As I know, it's recomended to use classes, ie:
enum object_type_t {
  OT_OPERATOR,
  OT_SERVICE,
  OT_PRICE,
  ...
}

class tObject {
  object_type_t d;
};

class tOperator extends tObject {
  string code;
  string name;
};

interface ObjectManipulator {
  bool createObject(out obj_id_t objid, tObject obj);
  bool modifyObject(obj_id_t objid, tObject obj);
  bool deleteObject(obj_id_t objid, object_type_t objtype);
};

It looks very good. But I've got a problem with deal with this in servant and client. First, to create object I have to do in client:
tOperator* obj = new tOperator();
obj.name = "Woohaa Com";
obj.code = "WHC";

obj.d = OT_OPERATOR; // right?

obj_id_t* id;

createObject(id, obj);

In servant I have to do:
switch (obj.d) {
  case OT_OPERATOR:
    tOperatorPtr p = tOperatorPtr::dynamicCast(obj);
    if (p)
      createOperator(*p);
    else
      throw runtime_error("Invalid object type");
    break;
}

Is this all right? If it is, then if something will be wrong with tObject.d type indicator it will be known not earlier then on run time. With CORBA unions this kind of errors give me a compiler errors. Second, It gives much more bloated code than before, with CORBA...

I know, I can handle this with tObject factory, but this requires additional effort and I don't have any guarantee that client developers will do it. So I think I should reanalyze this solution once again.

Can you help me how to do it proper with ICE?

thanks in advance.

Comments

  • bernard
    bernard Jupiter, FL
    Hi Radoslaw,

    Welcome to our forums. In general, I would recommend to use polymorphism instead of a switch with many cases ... it's safer, looks nicer and is more maintainable.

    For example, class tObject could be:
    class tObject {
      boolean create(out obj_id_t);
    
      object_type_t d; // maybe not needed anymore
    };
    

    and then createObject becomes (note that with Ice/Slice, out parameters must follow in parameters):
    bool createObject(const tObjectPtr& obj, obj_id_t& objid, const Ice::Current&)
    {
        return obj->create(objid);
    }
    

    Now, maybe you don't want to expose this create() to clients who don't need/want to implement it: they just want a simple data structure.

    In this case, keep the old operation-less tObject, but register *on your server* factories for tOperator/tService (...) that create tObjectI instances:
    class tObjectI : public virtual tObject
    {
    public:
       virtual bool create(obj_id_t&);
    };
    

    createObject then becomes:
    bool createObject(const tObjectPtr& obj, obj_id_t& objid, const Ice::Current&)
    {
        tObjectI* objI = dynamic_cast<tObjectI*>(obj.get());
        assert(objI != 0);
        return objI->create(objid);
    }
    

    Best regards,
    Bernard