Publicado em: 27/Dec/2019
Atualizado em: 03/Jan/2020
IDL
C++

CORBA

Introdução

O que é CORBA?

  • é o acrônimo para Common Object Request Broker Architecture
  • é um framework para construção de sistemas distribuídos orientados a objetos
  • é multiplataforma
  • é independente de linguagem
  • é um padrão aberto e extensível definido pela Object Management Group
  • clientes e servidores estão em diferentes máquinas
  • programas clientes enviam mensagens para servidores remotos como se este estivesse locais (location transparency)

O que é a OMG?

  • www.omg.org
  • fundada em 1989
  • somente provê especificações
  • não provê implementações
  • todas as especificações são free

CORBA vs SOAP/XML-RPC

  • SOAP e XML-RPC são protocolos wire
  • CORBA inclui:
    • um protocolo wire
    • GIOP
    • object model
    • IDL
    • mapeamento de linguagem
    • serviços
    • códigos de cliente e servidor portáveis entre implementações CORBA

CORBA é pesado?

ORB - Object Request Broker

  • é o núcleo da Object Management Architecture
  • integra:
    • diferentes linguagens
    • diferentes arquiteturas
    • diferentes sistemas operacionais
  • é responsável por:
    • encontrar uma implementação para uma requisição
    • preparar implementações para receber requisições
    • comunicar dados das requisições

Clientes

  • possuem referências para objetos e invocam operações nestes objetos
  • conhecem apenas a interface dos objetos do servidor
  • não conhecem a implementação dos objetos nem os adaptadores utilizados por estas implementações
  • invocam interfaces definidas por uma IDL
  • invocam as implementações através de:
    • proxies (IDL generated stubs)
    • DII (Dynamic Invocation Interface)

Servidores

  • se registram no ORB
  • se disponibilizam para aceitar requisições
  • implementam uma IDL

IDL

  • provê interfaces independente de linguagem e sistema operacional
  • é puramente declarativa (não provê detalhes de implementação)
  • é fortemente tipada
  • especificações podem ser escritas e invocadas em qualquer linguagem

IDL Features

  • modules
  • interfaces
  • operations
  • attributes
  • inheritance
  • basic types
  • arrays
  • sequences
  • struct, enum, union
  • typedef
  • consts
  • exceptions

Tipos básicos

  • float
  • double
  • long
  • short
  • unsigned long
  • unsigned short
  • char
  • boolean
  • octet
  • any

Direções dos parâmetros

  • in - do cliente para o servidor
  • out - do servidor para o cliente
  • inout - do cliente para o servidor e de volta para o cliente

Exceções

1Interface Bank {
2  exception Reject {
3    string reason;
4  };
5  exception TooMany {};
6  Account newAccount( in string name ) raises (Reject, TooMany);
7};

Operações oneway

1Interface Account {
2  oneway void notice( in string notice );
3};
  • não bloqueantes
  • não aceitam parâmetros out ou inout
  • não lançam exceções (não aceitam raise)

Estruturas

1struct PersonalDetails {
2  string name;
3  short age;
4};
5
6interface Bank {
7  PersonalDetails getPerDet(in string name);
8};

Arrays

  • podem ser multidimensionais
  • precisam ter tamanho fixo (conhecido em tempo de definição)
1Account bankAccounts[100];
2short matrix[10][20]; // 2D array

Constantes

1Interface Bank {
2  const long MaxAccounts = 10000;
3};

Contantes dos tipos long, float e string podem ser declarada.

Typedef

1typedef short size;
2size i;
3
4typedef Account Accounts[100]; // acho que está invertido aqui
5Accounts bankAccounts;

Módulos

1Module Finance {
2  interface Bank { ... };
3  interface Account { ... };
4};
  • são usados para agrupar interfaces em unidades lógicas
  • utilize nomes completos (p.e. Finance::Account *a;)

Preprocessador

  • baseado no preprocessador do C++
  • substituição de macro
  • compilação condicional
  • inclusão de IDL
  • #include
  • #define
  • #if
  • #ifdef
  • #defined

Mapeamento para linguagem

  • linguagens OO e non-OO acessam de modos diferentes
  • tipos específicos da linguagem
  • estrutura do stub cliente (somente para non-OO)
  • interface de invocação dinâmica
  • esqueleto de implementação
  • adaptadores de objeto
  • interface direta ao ORB

Mapeamento de identificadores

  • usa o mesmo nome
  • se for uma palavra reservada do C++ receberá um _ como prefixo (p.e. _new)

Mapeamento de interface

  • são mapeadas para classes
  • uma IDL mapeada para uma classe C++ não pode ser instanciada
1Interface Account { ... }

será mapeado para:

1class Account : public virtual CORBA::Object { ... }

Mapeamento de escopo

Interface para classe

1Interface Bank {
2  struct Details { ... };
3};

será mapeado para:

1class Bank {
2  public:
3    struct Details { ... };
4};

Module para namespace

1Module M {
2  Interface A { ... };
3  Interface B { ... };
4};

será mapeado para:

1namespace M {
2  class A { ... };
3  class B { ... };
4};

Acessíveis via M::A e M::B.

Mapeamento do módulo padrão do CORBA

Será mapeado para:

1namespace CORBA { ... }

Utilize os membros como:

1CORBA::ORB_init( ... );

Mapeamento de tipos

IDL C++ Tamanho e faixa
short CORBA::Short 16 bit: $-2^{15} \ldots +2^{15} - 1$
long CORBA::Long 32 bit: $-2^{31} \ldots +2^{31} - 1$
long long CORBA::LongLong 64 bit: $-2^{63} \ldots +2^63 - 1$
unsigned short CORBA::UShort 16 bit: $0 \ldots 2^{16} - 1$
unsigned long CORBA::ULong 32 bit: $0 \ldots 2^{32} - 1$
unsigned long long CORBA::ULongLong 64 bit: $0 \ldots 2^{64}$
float CORBA::Float 32 bit IEEE single precision floating point number
double CORBA::Double 64 bit IEEE double precision floating point number
long double CORBA::LongDouble
char CORBA::Char 8 bit
wchar CORBA::WChar – (Wide Char)
string CORBA::char *
wstring CORBA::WChar *
boolean CORBA::Boolean true/false
octet CORBA::Octet (unsigned char) 8 bit raw. Sem conversão
any CORBA::Any Arbitrary

Repositório de interfaces

  • provê armazenamento para informações da IDL
  • um programa pode referenciar objetos cuja interface será conhecida em tempo de execução
  • estas informações podem ser usadas pelo ORB para realizar requisições
  • pode armazenar outras informações sobre interfaces:
    • debugging
    • info
    • browser routines

Repositório de implementação

  • informações que o ORB utiliza para localizar e ativar implementações de um servidor de objetos
  • armazena informações de ativação:
    • máquina onde um servidor pode ser iniciado a partir de uma requisição do cliente

Dynamic Invocation Interface

  • Em vez de chamar um stub específico de uma operação, é possível especificar um objeto, operação e parâmetros para através de uma chamada ou sequência de chamadas
  • o cliente precisa fornecer os tipos dos parâmetros passados

Interoperabilidade

  • suporta rede de objetos distribuídos em múltiplos ORBs (heterogêneos)
  • InterORBability
  • GIOP: (General Inter-ORB Protocol) sintaxe padrão de transferência e conjunto de formato de mensagens
  • ESIOP: (Environment Specific Inter-ORB Protocol) protocolo especializado
  • IIOP: mapeamento TCP/IP para GIOP

COSS - Common Object Service Specification

  • naming
  • events
  • life cycle
  • time
  • transactions
  • concurrency
  • persistence
  • query
  • security
  • licensing
  • relationships
  • properties
  • externalization
  • collection

Instalação do omniORB no Debian

1apt install libomniorb4-dev omniidl omniorb omniorb-doc omniorb-nameserver omniidl-python python-omniorb

Iniciar o servidor de nomes

OmniORB

Incluir no arquivo /etc/omniORB.cfg a seguinte linha:

1InitRef = OmniNameService=corbaname::localhost
Nota

O nome OmniNameService deve ser conhecido pelo servidor e pelo cliente.

Para iniciar o servidor de nomes do OmniORB use:

1omniNames -start

Exemplo de saída do comando acima:

1omniNames: (0) 2019-12-30 15:36:29.516974: Data file: '/var/lib/omniorb/omninames-erebo.dat'.
2omniNames: (0) 2019-12-30 15:36:29.517058: Starting omniNames for the first time.
3omniNames: (0) 2019-12-30 15:36:29.517348: Wrote initial data file '/var/lib/omniorb/omninames-erebo.dat'.
4omniNames: (0) 2019-12-30 15:36:29.517444: Read data file '/var/lib/omniorb/omninames-erebo.dat' successfully.
5omniNames: (0) 2019-12-30 15:36:29.517529: Root context is IOR:010000002b00000049444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f6e746578744578743a312e300000010000000000000070000000010102000d0000003139322e3136382e302e31330000f90a0b0000004e616d6553657276696365000300000000000000080000000100000000545441010000001c000000010000000100010001000000010001050901010001000000090101000354544108000000ad430a5e010075e0
6omniNames: (0) 2019-12-30 15:36:29.517600: Checkpointing Phase 1: Prepare.
7omniNames: (0) 2019-12-30 15:36:29.517703: Checkpointing Phase 2: Commit.
8omniNames: (0) 2019-12-30 15:36:29.517802: Checkpointing completed.

A porta padrão é a 2809.

Os arquivos de controle serão criados por padrão no diretório /var/lib/omniorb/:

  • /var/lib/omniorb/omninames-nome-da-maquina.dat
  • /var/lib/omniorb/omninames-nome-da-maquina.bak

Para especificar um diretório diferente use a opção -logdir:

1omniNames -logdir /tmp -start

MICO

Para iniciar o servidor de nomes do MICO use:

1nsd -ORBIIOPAddr inet:localhost:2809

Veja exemplo em Connecting 3 ORBs.

Exemplo

Componentes do exemplo
Componentes do exemplo

Criando a IDL

Arquivo de interface Data.idl:

 1#ifndef __DATADIST_IDL__
 2#define __DATADIST_IDL__
 3module Data
 4{
 5  interface ServiceA {
 6    boolean CallServiceRoutineA (
 7        in    long num1,
 8        inout long num2,
 9        out   long retNum );
10    boolean CallServiceRoutineB (
11        inout long num1,
12        inout long num2);
13  };
14};
15#endif

Gerando backend a partir da IDL

Use a opção -b para escolher o backend a ser gerado.

1omniidl -bcxx    Data.idl
2omniidl -bpython Data.idl
3omniidl -bdump   Data.idl

Gerando Python

1omniidl -bpython Data.idl

os arquivos abaixo são criados:

  • Data_idl.py
  • Data__POA/__init__.py
  • Data/__init__.py
FIXME: Este trecho será revisado

Descrever os arquivos gerados em python.

Gerando C++

Ao executar o seguinte comando:

1omniidl -bcxx Data.idl

os arquivos abaixo são criados:

  • Data.hh - cabeçalho que será usado pelo cliente e pelo servidor
  • DataSK.cc - deve ser compilado e lincado com o programa que proverá a infraestrutura de comunicação

Contendo os seguintes items:

  • classe ServiceA contendo funções estáticas e definições de tipos
  • ServiceA_ptr - um ponteiro para o tipo do objeto de referência
  • ServiceA_var - um helper para gerenciamento de memória usado por ServiceA_ptr
  • classe POA_Service! - a classe esqueleto para o servidor
Data.hh
85  // interface ServiceA
86  class ServiceA {
87  public:
88    // Declarations for this interface type.
89    typedef ServiceA_ptr _ptr_type;
90    typedef ServiceA_var _var_type;
91    static _ptr_type _duplicate(_ptr_type);
92    static _ptr_type _narrow(::CORBA::Object_ptr);
93    static _ptr_type _unchecked_narrow(::CORBA::Object_ptr);
94    static _ptr_type _nil();
DataSK.cc
 1// This file is generated by omniidl (C++ backend)- omniORB_4_2. Do not edit.
 2
 3#include "Data.hh"
 4#include <omniORB4/IOP_S.h>
 5#include <omniORB4/IOP_C.h>
 6#include <omniORB4/callDescriptor.h>
 7#include <omniORB4/callHandle.h>
 8#include <omniORB4/objTracker.h>
 9
10OMNI_USING_NAMESPACE(omni)
11...

Criando o servidor

Server.cc

Código do servidor:

  1. Iniciar o ORB
  2. Obter a referência para o POA raiz para registra-se
  3. Vincular com o serviço de nomes
  4. Iniciar o servidor de objetos
 1// Server.cc
 2
 3#include <assert.h>
 4#include <iostream>
 5#include <signal.h>
 6#include <stdlib.h>
 7#include <string>
 8#include <unistd.h>
 9#include "CServiceA_i.hh"
10#include "Data.hh"
11
12using namespace std;
13
14int main( int argc, char **argv )
15{
16  try {
17    // (1) Iniciar o ORB
18    CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );
19    // (2) Obter a referência para o POA raiz para registra-se para ficar disponível para os clientes
20    CORBA::Object_var       obj  = orb->resolve_initial_references( "RootPOA" );
21    PortableServer::POA_var _poa = PortableServer::POA::_narrow( obj.in() );
22    // As operações definidas na interface são invocadas pelo objeto de referência.
23    // Um instância de CRequestSocketStream_i é iniciada
24    PortableServer::Servant_var<CServiceA_i> myRequestServiceA = new CServiceA_i();
25    // o servidor e objetos é ativado no RootPOA
26    PortableServer::ObjectId_var myRequestServiceA_oid = _poa->activate_object( myRequestServiceA );
27    // Obtêm o objeto de referência do servidor e registra no servidor de nome
28    CORBA::Object_var SA_obj = myRequestServiceA->_this();
29    // Obtêm uma referência para o objeto e imprime seu IOR como string
30    CORBA::String_var sior( orb->object_to_string( SA_obj.in() ) );
31    cerr << "'" << (char *)sior << "'" << endl;
Nota

Aqui você deve utilizar o mesmo nome do servidor de nomes definido no arquivo /etc/omniORB.cfg.

32    // (3) Vincular com o serviço de nomes
33    // o serviço é definido pela diretiva InitRef e pelo identificador
34    // "OmniNameService" no arquivo omniORB.cfg
35    CORBA::Object_var obj1 = orb->resolve_initial_references( "OmniNameService" );
36    assert( !CORBA::is_nil( obj1.in() ) );
37    CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow( obj1.in() );
38    assert( !CORBA::is_nil( nc.in() ) );
39    CosNaming::Name name;
40    name.length( 1 );
41    name[0].id = CORBA::string_dup( "DataServiceName1" );
42    nc->rebind( name, SA_obj.in() );
43    //========================================================================
44    myRequestServiceA->_remove_ref();
45    //
46    PortableServer::POAManager_var pmgr = _poa->the_POAManager();
47    pmgr->activate();
48    // (4) Iniciar o servidor de objetos para aceitar requisições dos clientes
49    orb->run();
50    //
51    // Se o ORB deixar o loop de tratamento de eventos
52    // Atualmente está configurado para nunca dar timeout
53    orb->destroy();
54    //
55    free( name[0].id ); // str_dup realiza um malloc internamente
56  }

Tratar as exceções:

57  catch( CORBA::TRANSIENT & ) {
58    cerr << "Caught system exception TRANSIENT -- unable to contact the server." << endl;
59  }
60  catch( CORBA::OBJECT_NOT_EXIST & ) {
61    cerr << "Caught system exception OBJECT_NOT_EXIST" << endl;
62  }
63  catch( CORBA::SystemException & ) {
64    cerr << "Caught CORBA::SystemException." << endl;
65  }
66  catch( CORBA::Exception & ) {
67    cerr << "Caught CORBA::Exception." << endl;
68  }
69  catch( omniORB::fatalException &fe ) {
70    cerr << "Caught omniORB::fatalException:" << endl;
71    cerr << "  file: " << fe.file() << endl;
72    cerr << "  line: " << fe.line() << endl;
73    cerr << "  mesg: " << fe.errmsg() << endl;
74  }
75  catch( ... ) {
76    cerr << "Caught unknown exception." << endl;
77  }
78
79  return 0;
80}
  • O POA (Portable Object Adapter) auxilia o ORB (Object Request Broker) na passagem das requisições do clientes para a implementação do servidor (servant).
  • O POA interpreta a requisição, empacota a passagem de parâmetros e então localiza o servant e os tratadores de recuperação erros e de segurança.
  • Isto habilita portabilidade, independência de fabricantes e extensibilidade compatível com a especificação da OMG (Object Management Group)
  • POA também habilita a persistência de objetos bem como dá suporte a ciclo de vida do serviço

Implementação do Servant

São denotados pelo sufixo _i.

CServiceA_i.hh

 1// CServiceA_i.hh
 2#include "Data.hh"
 3
 4class CServiceA_i : public POA_Data::ServiceA, public PortableServer::RefCountServantBase {
 5public:
 6  CServiceA_i();
 7  virtual ~CServiceA_i();
 8  virtual CORBA::Boolean CallServiceRoutineA(
 9    /*in*/    CORBA::Long num1,
10    /*inout*/ CORBA::Long &num2,
11    /*out*/   CORBA::Long &retNum );
12  virtual CORBA::Boolean CallServiceRoutineB(
13    /*inout*/ CORBA::Long &num1,
14    /*inout*/ CORBA::Long &num2 );
15};

CServiceA_i.cc

 1// CServiceA.cc
 2
 3#include <fstream>
 4#include <iostream>
 5#include <stdio.h>
 6#include <stdlib.h>
 7#include <string.h>
 8#include <string>
 9#include <sys/types.h>
10#include <unistd.h>
11#include <vector>
12
13#include "CServiceA_i.hh"
14
15using namespace Data;
16
17#include <sys/wait.h>
18
19CServiceA_i::CServiceA_i() { }
20
21CServiceA_i::~CServiceA_i( void ) { }
22
23CORBA::Boolean CallServiceRoutineA(
24    /*in*/    CORBA::Long num1,
25    /*inout*/ CORBA::Long &num2,
26    /*out*/   CORBA::Long &retNum )
27{
28  num2   = num2 + num1;
29  retNum = 10;
30  return true;
31}
32
33CORBA::Boolean CallServiceRoutineB(
34    /*inout*/ CORBA::Long &num1,
35    /*inout*/ CORBA::Long &num2 )
36{
37  num1++;
38  num2++;
39  return true;
40}

Cliente CORBA

Cliente CORBA
Cliente CORBA

Client.cc

 1// Client.cc
 2#include "CRequestServiceA.hh"
 3
 4int main( int argc, char **argv )
 5{
 6  // O construtor estabelece o link como o servidor CORBA.
 7  CRequestServiceA requestServiceA;
 8
 9  if( requestServiceA.RequestServiceARoutineA() ) {
10    cout << "ServiceA RoutineA: True" << endl;
11  }
12  if( requestServiceA.RequestServiceARoutineB() ) {
13    cout << "ServiceA RoutineB: True" << endl;
14  }
15  return 0;
16}

CRrquestServiceA.hh

 1// CRrquestServiceA.hh
 2#include "Data.hh"
 3#include <cassert>
 4#include <fstream>
 5#include <iostream>
 6
 7using namespace std;
 8
 9class CRequestServiceA {
10public:
11  CRequestServiceA();
12  ~CRequestServiceA();
13  bool RequestServiceARoutineA();
14  bool RequestServiceARoutineB();
15
16  CosNaming::Name m_corbaCosName;
17
18  // CORBA ORB
19  CORBA::ORB_var m_orb;
20
21  CORBA::Object_var m_obj;  // ORB Object
22  CORBA::Object_var m_obj1; // Resolved id to object reference
23
24  // Resolved and narrowed CORBA object for proxy calls
25  Data::ServiceA_var m_Data;
26};
27
28class DS_ServerConnectionException {
29public:
30  DS_ServerConnectionException()
31  {
32    cerr << "CORBA COMM_FAILURE" << endl;
33  };
34};
35
36class DS_SystemException {
37public:
38  DS_SystemException()
39  {
40    cerr << "CORBA Exception" << endl;
41  };
42};
43
44class DS_FatalException {
45public:
46  DS_FatalException()
47  {
48    cerr << "CORBA Fatal Exception" << endl;
49  };
50};
51
52class DS_Exception {
53public:
54  DS_Exception()
55  {
56    cerr << "Exception" << endl;
57  };
58};

CRequestServiceA.cc

  1// CRequestServiceA.cc
  2
  3#include "CRequestServiceA.hh"
  4using namespace Data;
  5
  6CRequestServiceA::CRequestServiceA()
  7{
  8  try {
  9    // Inicia o objeto ORB
 10    int            argc = 0; // Variáveis dummy.
 11    char **        argv = 0;
 12    CORBA::ORB_var orb  = CORBA::ORB_init( argc, argv );
 13
 14    // Vincula o objeto ORB ao servidor de nome
 15    // Deve ser o mesmo nome definido em /etc/omniORB.cfg
 16    CORBA::Object_var obj = orb->resolve_initial_references( "OmniNameService" );
 17    assert( !CORBA::is_nil( obj.in() ) );
 18
 19    CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow( obj.in() );
 20    assert( !CORBA::is_nil( nc.in() ) );
 21
 22    CosNaming::Name _corbaCosName;
 23    _corbaCosName.length( 1 );
 24    _corbaCosName[0].id = CORBA::string_dup( "DataServiceName1" );
 25
 26    CORBA::Object_var obj1 = nc->resolve( _corbaCosName );
 27    assert( !CORBA::is_nil( obj1.in() ) );
 28
 29    m_Data = ServiceA::_narrow( obj1.in() );
 30    if( CORBA::is_nil( m_Data.in() ) ) {
 31      cerr << "IOR is not an SA object reference." << endl;
 32    }
 33  }
 34  catch( CORBA::COMM_FAILURE &ex ) {
 35    cerr << "Caught system exception COMM_FAILURE -- unable to contact the "
 36         << "object." << endl;
 37    throw DS_ServerConnectionException();
 38    return;
 39  }
 40  catch( CORBA::SystemException & ) {
 41    cerr << "Caught a CORBA::SystemException." << endl;
 42    throw DS_SystemException();
 43    return;
 44  }
 45  catch( CORBA::Exception & ) {
 46    cerr << "Caught CORBA::Exception." << endl;
 47    throw DS_Exception();
 48    return;
 49  }
 50  catch( omniORB::fatalException &fe ) {
 51    cerr << "Caught omniORB::fatalException:" << endl;
 52    cerr << "  file: " << fe.file() << endl;
 53    cerr << "  line: " << fe.line() << endl;
 54    cerr << "  mesg: " << fe.errmsg() << endl;
 55    throw DS_FatalException();
 56    return;
 57  }
 58  catch( ... ) {
 59    cerr << "Caught unknown exception." << endl;
 60    throw DS_Exception();
 61    return;
 62  }
 63  return;
 64}
 65
 66CRequestServiceA::~CRequestServiceA()
 67{
 68  // ...
 69}
 70
 71bool CRequestServiceA::RequestServiceARoutineA()
 72{
 73  CORBA::Long num1 = 4;
 74  CORBA::Long num2 = 5;
 75  CORBA::Long retNum;
 76
 77  cout << "Valores de entrada para a rotina A do serviço A: " << num1 << " " << num2 << " " << retNum << endl;
 78
 79  // Esta chamada CORBA será executada remotamente
 80  if( m_Data->CallServiceRoutineA( num1, num2, retNum ) ) {
 81    cout << "Valores retornados pelo serviço A: " << num1 << " " << num2 << " " << retNum << endl;
 82    return true;
 83  }
 84  return false;
 85}
 86
 87bool CRequestServiceA::RequestServiceARoutineB()
 88{
 89  CORBA::Long num1 = 0;
 90  CORBA::Long num2 = 50;
 91
 92  cout << "Valores de entrada para a rotina B do serviço A: " << num1 << " " << num2 << endl;
 93
 94  // Esta chamada CORBA será executada remotamente
 95  if( m_Data->CallServiceRoutineB( num1, num2 ) ) {
 96    cout << "Valores retornados pelo serviço B: " << num1 << " " << num2 << endl;
 97    return true;
 98  }
 99  return false;
100}

Makefile

 1# Makefile
 2LDFLAGS+=-lomniORB4 -lomnithread -lomniDynamic4
 3CXXFLAGS+=-Wall
 4
 5all: server client
 6
 7server: DataSK.o CServiceA_i.o Server.o
 8	$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
 9
10clean:
11	rm -f *.o
12	rm -f server
13
14client: DataSK.o Client.o CRequestServiceA.o
15	$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
16

This is the value of CORBA. This is why we use CORBA. Look at how simple the program looks. The constructor establishes the link with the CORBA server. The subsequent calls to Routine A and B are processed remotely on the CORBA server but called like any other local function call.

Todo

Referências

comments powered by Disqus