Tecgraf PUC-Rio
Outubro de 2013
CORBA C++
Definição e História
• O que é o mapeamento CORBA C++ ?
– Documento que padroniza como as declarações de uma IDL são mapeadas para a linguagem C++
• antes de 1994
– Mapeamentos proprietários
• Desafio: linguagem multiparadigma
• 1994
– Primeira versão não publicada
– Anterior ao primeiro padrão(98) de C++!
• 2012
– Mapeamento(v1.3) para C++11 :)
Identificadores e módulos
• Identificadores
– São preservados com exceção de palavras reservadas da linguagem C++
• prefixo: _cxx_
• Módulos
– Mapeamento para namespaces – Módulo CORBA
//IDL
module A { /* definitions */ };
module B { /* definitions */ };
module A { /* more definitions */ };
//C++
namespace A { /* ... */ } namespace B { /* ... */ } namespace A { /* ... */ }
Tipos básicos
IDL C++ Size
short CORBA::Short >= 16 bits
long CORBA::Long >= 32 bits
long long CORBA::LongLong
unsigned short CORBA::UShort >= 16 bits unsigned long CORBA::ULong >= 32 bits unsigned long long CORBA::ULongLong
float CORBA::Float >= 32 bits
double CORBA::Double >= 64 bits
long double CORBA::LongDouble
char CORBA::Char >= 8 bits
wchar CORBA::WChar
boolean CORBA::Boolean Unspecified
octet CORBA::Octet >= 8 bits
Tipos básicos - overloading
• Todos os tipos básicos são mapeados para tipos C++ distinguíveis, exceto: char, boolean, octet e wchar
– Exemplo:
namespace CORBA { typedef char Char;
typedef char Boolean;
}
void fn(CORBA::Char) { }
void fn(CORBA::Boolean { } // Ops!
string
• Mapeamento de strings para char *
• Wide strings para CORBA::WChar *
• Não usar o par new/delete !
//Alocação de (len+1) bytes.
//Retorno de um ponteiro nulo em caso de falha na alocação.
static char * string_alloc(CORBA::ULong len);
//Alocação e cópia
static char * string_dup(const char *);
//Desalocação
void string_free(char *);
const
• Mapeamento para constantes C++
//IDL
const string s = "str";
module A {
const short s = 20;
};
interface X {
const long l = 10;
};
//C++
static const char *const s = “str”;
namespace A {
const CORBA::Short s = 20;
}
class X { public:
static const CORBA::ULong l = 10;
};
enum
• Mapeamento para uma enumeração C++ (enum)
• Não se deve utilizar os valores ordinários
//IDL e C++
enum color { red, green, blue };
Tipos de tamanho variável
• Necessidade de se utilizar uma alocação dinâmica
– String
– Object reference – Any
– Sequence
– Struct (elemento de tamanho variável)
– Array (elemento de tamanho variável)
Gerenciamento da memória
• O que há de errado com o código abaixo ?
{
char *s = CORBA::string_dup(“str”);
foo();
string_free(s);
}
Gerenciamento da memória
• Uso de smart pointers para evitar memory leaks e facilitar o uso.
– RAII (Resource Acquisition Is Initialization) – std::auto_ptr
– std::unique_ptr, std::shared_ptr e std::weak_ptr
{
CORBA::String_var s(“str”);
foo();
}
Gerenciamento da memória
struct T_var { T_var();
T_var(T *);
T_var(const T_var &);
~T();
T_var & operator=(T *);
T_var & operator=(const T_var &);
T * operator->();
const T * operator->() const;
/* ... */
private:
T *p_;
};
Gerenciamento da memória
/* Inicializa o ponteiro interno como nulo. */
T_var();
/* Assume o ownership da instância apontada por p, que deve apontar para uma área de memória alocada dinamicamente. */
T_var(T *p);
/* Deep copy */
T_var(const T_var &);
/* Desaloca a instância apontada por p_ se ninguém mais estiver utilizando o recurso. */
~T_var();
/* Desaloca a instância apontada por p_ e assume o ownership da instância apontada por p */
T_var & operator=(T *);
/* Desaloca a instância apontada por p_ e faz deep copy de p */
T_var & operator=(const T_var &);
Gerenciamento da memória
/* Uso como ponteiro */
T * operator->();
const T * operator->() const;
/* Uso como referência */
operator T &();
const operator T &() const;
/* sequence ou array */
T & operator[](CORBA::ULong idx);
const T & operator[](CORBA::ULong idx) const;
Gerenciamento da memória
• Tipos de tamanho variável
operator T *&();
const T & in() const;
T & inout();
T *& out();
T * _retn();
• Tipos de tamanho fixo
T_var(const T &);
T_var & operator=(const T &);
const T & in() const;
T & inout();
T & out();
T _retn();
Gerenciamento da memória - string
String_var(); \\internal_pointer_ = 0
\\assume ownership String_var(char *);
String_var &operator=(char *);
\\deep copy
String_var(const char*);
String_var &operator=(const char *);
\\assume ownership
String_var(const String_var &);
String_var &operator(const String_var &);
~String_var(); \\string_free()
Gerenciamento da memória - string
\\conversão implícita
operator const char*() const;
operator char*&();
operator char*();
\\conversão explícita const char* in() const;
char*& inout();
char*& out(); \\string_free()
char* _retn(); \\yielding ownership char &operator[](CORBA::ULong index);
char operator[](CORBA::ULong index) const;
Gerenciamento da memória - string
CORBA::String_var ret = fn();
char *fn() {
CORBA::String_var s(CORBA::string_dup(str));
return s; \\Ops! Double free!
}
char *fn() {
CORBA::String_var s(CORBA::string_dup(str));
return CORBA::string_dup(s); \\Ok }
char *fn() {
CORBA::String_var s(CORBA::string_dup(str));
return s._retn(); \\Ok }
Gerenciamento da memória - string
• Strings declaradas em tipos definidos pelo usuário(struct, sequence, exception e array) são inicializadas com string vazia
• Necessidade de métodos para alocação e desalocação para evitar definições globais de operator new[] e operator delete[]
struct
• Mapeamento para uma struct de C++. Membros seguem as suas respectivas regras de mapeamento
\\IDL
struct A { double d;
string s;
};
\\C++
struct A {
CORBA::Double d;
CORBA::String_mgr s;
};
class A_var;
sequence
• Mapeamento para uma classe C++ que se comporta como um vetor de tamanho variável
\\IDL
typedef sequence<string> str_seq;
sequence
• Mapeamento para uma classe C++ que se comporta como um vetor de tamanho variável
– O número máximo de elementos é somente uma dica!
• Não há garantia que:
– os elementos serão pré-alocados no mesmo instante;
– serão alocados exatamente max elementos;
– os elementos ocuparão uma região contígua da memória.
• Cuidado ao manter ponteiros para sequences por conta de reallocations!
str_seq(); \\empty sequence str_seq(CORBA::ULong max);
sequence
\\deep copy
str_seq(const str_seq &);
str_seq & operator=(const str_seq &);
\\membros de tamanho variável são liberados
~str_seq();
sequence
\\número de elementos
CORBA::ULong str_seq::length() const;
\* aloca (len - length()) elementos ou trunca (length() - len) elementos. Novos elementos inicializados com default construtor ou string vazia se for o caso
*/
void str_seq::length(CORBA::ULong len);
• Indexação de 0 até length()-1
• Acesso fora do intervalo acima é undefined behavior
sequence
CORBA::String_mgr & operator[](CORBA::ULong idx);
const char * operator[](CORBA::ULong idx) const;
• Mapeamento para um array C++
array
//IDL
typedef string str_array[10];
//C++
typedef CORBA::String_mgr str_array[10];
typedef CORBA::String_mgr str_array_slice;
\\retorna null em caso de falha
str_array_slice * str_array_alloc();
str_array_slice * str_array_dup(const str_array_slice *);
void str_array_free(str_array_slice *);
void str_array_copy(str_array_slice *to, const str_array_slice * from);
• Mapeamento para um typedef C++
typedef
//IDL
typedef string str_array[4];
typedef str_array address;
//C++
/* declarações para str_array */
typedef str_array address;
typedef str_array_slice address_slice;
address_slice * address_alloc() { return address_alloc(); } address_slice * address_dup(const address_slice *p)
{ return str_array_dup(p); }
void address_free(address_slice *p) { str_array_free(p); }
void address_copy(address_slice *to, const address_slice * from) { str_array_copy(to, from); }
• Mapeamento para uma classe C++ que atua como um proxy
interface
//IDL
interface my_interface { void foo();
};
//C++
struct my_interface
: public virtual CORBA::Object {
virtual void foo() = 0;
};
• O uso de exception specifications é opcional
my_interface obj; //Errado my_interface *obj; //Errado
void f(my_interface &); //Errado my_interface_ptr obj; //Certo my_interface_var obj; //Certo
• Mapeamento para um par de métodos do tipo get e set
atributos
//IDL
interface my_interface { attribute long a;
readonly attribute long b;
};
//C++
virtual CORBA::Long a() = 0;
virtual void a(CORBA::Long val) = 0;
virtual CORBA::Long b() = 0;
• Mapeamento para uma classe que herda de CORBA::UserException
exception
//IDL
exception ex { string msg;
};
//C++
struct ex :
public CORBA::UserException {
CORBA::String_mgr msg;
ex(const char *msg);
};
• Mapeamento para a classe CORBA::Any que armazena internamente um type code e o valor de interesse
• Inserção de um valor através de operator<<=
• deep copy
any
//C++
CORBA::Any a;
a <<= “Hello”;
a <<= “World”; //desaloca “Hello” e copia “World”
a <<= CORBA::string_dup(“str”) //memory leak!
• Extração de um valor através de operator>>=
• Verificação do tipo durante a operação
any
//C++
CORBA::Any a;
a << static_cast<CORBA::Long>(99);
CORBA::Long val;
bool ret = (a >>= val);
assert(ret);
• from_boolean(), to_boolean(), from_char(), to_char(), from_octet() e to_octet()
• Any retêm o ownership de uma string e entrega um ponteiro para um área de memória interna
• in, inout, out (tamanho fixo), out (tamanho variável) e valor de retorno
• caller (quem faz a chamada chamada) e callee (quem recebe a chamada)
• O caller sempre é responsável por desalocar!
Passagem de parâmetros
1. Caller faz a alocação e inicialização do parâmetro 2. Faz a chamada
3. ORB copia o parâmetro para o espaço de endereçamento do callee (no caso de uma chamada remota)
4. Callee obtêm um acesso somente de leitura a uma cópia local 5. Callee retorna
6. ORB desaloca a cópia (no caso de uma chamada remota)
7. Caller desaloca o parâmetro
Passagem de parâmetros - in
void foo(T_in);
1. Caller faz a alocação e inicialização do parâmetro 2. Faz a chamada
3. ORB copia o parâmetro para o espaço de endereçamento do callee (no caso de uma chamada remota)
4. Callee obtêm um acesso leitura e escrita a uma cópia local.
(se a modificação necessitar de um espaço maior uma realocação é realizada)
5. Callee retorna
6. ORB envia a cópia modificada de volta e desaloca a cópia. (O ORB do caller pode realocar e inicializar com o valor recebido do callee)
7. Caller desaloca o parâmetro
Passagem de parâmetros - inout
void foo(T_inout);
• T_out
• Tamanho fixo
• Tamanho variável
Passagem de parâmetros - T_out
//C++
struct String_out {
String_out(const char *&s) : ref_(s) { ref_ = 0; } String_out(const String_var &s) : ref_(s.ref_) { string_free(ref_);
ref_ = 0;
}
private:
char *& ref_;
};
typedef T &T_out;
1. Caller faz a alocação do parâmetro (tamanho fixo)
1. Caller define um ponteiro não inicializado como placeholder
(tamanho variável)
2. Faz a chamada
3. ORB de callee aloca o parâmetro 4. Callee inicializa o parâmetro
5. Callee retorna
6. ORB de callee envia o parâmetro de volta e o ORB de caller inicializa o parâmetro (no caso do tamanho variável o ORB de caller também aloca)
7. O ORB de callee desaloca o parâmetro 8. Caller desaloca o parâmetro
Passagem de parâmetros - out
void foo(T_out);
Passagem de parâmetros - valor de retorno
T foo();
• Segue as regras do parâmetro out
• Uso de _retn() para passar o ownership
char * name() {
String_var str = get_name();
foo(); //exception?!
return str._retn();
}
Passagem de parâmetros
IDL Type in inout out Return type
simple simple simple & simple & simple
enum enum enum & enum & enum
string const char * char *& char *& char *
any const Any & Any & Any *& Any *
objref objref_ptr objref_ptr & objref_ptr & objref_ptr sequence const sequence & sequence & sequence *& sequence * struct (fixed) const struct & struct & struct & struct struct (variable) const struct & struct & struct *& struct * array (fixed) const array array_slice * array_slice * array_slice * array (variable) const array array_slice * array_slice *& array_slice *
Passagem de parâmetros
IDL Type in inout/out return
string const String_var & String_var & String_var
any const Any_var & Any_var & Any_var
objref const objref_var & objref_var & objref_var sequence const sequence_var & sequence_var & sequence_var struct const struct_var & struct_var & struct_var array const array_var & array_var & array_var
Exemplo - Aplicação Stock Market
• IDL
– Compilação
• Servidor
– Implementação do servant
– Disponibilizar um CORBA Object
• IOR
• Cliente
– Interação com o objeto remoto
• IOR
StockMarket.idl
module StockMarket {
typedef string StockSymbol;
typedef sequence<StockSymbol> StockSymbolList;
interface StockServer {
float getStockValue(in StockSymbol symbol);
StockSymbolList getStockSymbols();
};
};
Servant
struct stock_market : public POA_StockMarket::StockServer {
CORBA::Float getStockValue(const char *symbol) {
/* ... */
}
StockMarket::StockSymbolList * getStockSymbols() {
StockMarket::StockSymbolList_var seq = new StockMarket::StockSymbolList;
seq->length(stocks_.size());
/* ... */
return seq._retn();
} };
Disponibilizar um CORBA Object
• Criar um ORB
• Obter o RootPOA
• Ativar o POA Manager
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
CORBA::Object_var o = orb->resolve_initial_references("RootPOA");
PortableServer::POA_var poa = PortableServer::POA::_narrow(o);
PortableServer::POAManager_var mgr = poa->the_POAManager();
mgr->activate();
Disponibilizar um CORBA Object
• Ativar o servant e criar um Object reference
• Disponibilizar a referência
• Escutar as requisições dos clientes
stock_market servant(s);
StockMarket::StockServer_var market = servant._this();
CORBA::String_var ior = orb->object_to_string(market);
std::cout << ior << std::endl;
orb->run();
Cliente
• Criar um ORB
• Criar um proxy para o objeto remoto dado um IOR
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
CORBA::Object_var o = orb->string_to_object(ior);
StockMarket::StockServer_var server = StockMarket::StockServer::_narrow(o);
StockMarket::StockSymbolList_var symbols = server->getStockSymbols();