Este é o comando rxgen que pode ser executado no provedor de hospedagem gratuita OnWorks usando uma de nossas múltiplas estações de trabalho online gratuitas, como Ubuntu Online, Fedora Online, emulador online de Windows ou emulador online de MAC OS.
PROGRAMA:
NOME
rxgen - Gerador de stub para o pacote de chamada de procedimento remoto Rx
SINOPSE
rxgen [-h | -c | -C | -S | -r] [-dkpR]
[-I dir] [-P prefixo] [-o arquivo de saída] [no arquivo]
rxgen -s transporte [-o arquivo de saída] [no arquivo]
rxgen -l [-o arquivo de saída] [no arquivo]
rxgen -m [-o arquivo de saída] [no arquivo]
DESCRIÇÃO
rxgen é uma ferramenta que gera código C para implementar o protocolo Rx RPC; leva como entrada
uma descrição de uma interface de aplicativo semelhante a C e produz uma série de servidores
e/ou rotinas stub de cliente a serem vinculadas a programas baseados em RPC. Esses stubs permitem
programas para invocar procedimentos remotos por meio de chamadas de procedimentos locais. rxgen é uma extensão
do Sol rpcgen (versão 3.9) e mantém completo rpcgen funcionalidade (pelo menos até então
versão). Consulte rpcgen(1) para mais detalhes sobre os sinalizadores específicos de RPC da Sun, e
consulte o guia de programação RPC sobre a linguagem RPC, juntamente com exemplos úteis.
OPÇÕES
rxgen opera em vários modos diferentes. Os arquivos de saída gerados podem ser produzidos
individualmente (usando um dos -h, -c, -Cou -S) ou coletivamente. Todos os arquivos de saída são
criado quando o padrão é usado (ou seja, sem opções) ou a saída é limitada ao
stubs de servidor (-C e -S) quando o -r bandeira é usada. A seguir descrevemos os tipos de
arquivos de saída gerados (para simplificar, nome do arquivo refere-se ao nome do arquivo de saída principal):
-h Gere definições de dados C (um arquivo de cabeçalho) a partir de definições RPCL padrão (padrão
extensão: nome do arquivo.h).
-c Compile as rotinas XDR necessárias para serializar o protocolo descrito por RPCL.
Gere rotinas XDR para todas as declarações (extensão padrão: nome do arquivo.xdr.c).
-C Gere todas as rotinas stub do lado do cliente (extensão padrão: nome do arquivo.cs.c).
Chamar uma rotina neste arquivo fará com que os argumentos sejam compactados e enviados via
Rx (ou R).
-S Gere todas as rotinas stub do lado do servidor (extensão padrão: nome do arquivo.ss.c).
Os argumentos são descompactados e a rotina do servidor correspondente é chamada.
-r Gere os dois arquivos de extensão padrão produzidos pelo -C e -S opções.
As seguintes opções podem ser usadas em qualquer combinação de rxgen ligações:
-R Gere código para o protocolo \R mais antigo, em oposição ao Rx, que é o padrão.
-k Deve ser especificado quando o código gerado for utilizado pelo kernel;
"inclui" especiais e outras especificidades são produzidas quando o resultado alvo é para o
núcleo.
-p Sinalizador de combinação de pacotes: quando vários pacotes são incluídos em um único
arquivo de especificação, uma única rotina Execute Request será usada para todos eles como um
resultado desta bandeira. O padrão é gerar stubs de solicitação de execução individuais para
cada pacote.
-I dir
Semelhante ao -I sinalizador no compilador C (cc). Este sinalizador é passado para o pré-
processador (cpp) para que esse diretório dir é pesquisado antes da lista de pesquisa padrão para
#incluir arquivos. Como esperado, vários -I sinalizadores podem ser usados simultaneamente.
-P prefixo
A prefixo a string após essa opção é anexada a todos os arquivos de saída gerados;
útil quando várias execuções desejam produzir versões diferentes da mesma interface
(digamos, versões kernel e não-kernel).
-d Modo de depuração; só é necessário quando rxgen deve ser depurado (digamos, via dbx).
-o arquivo de saída
Especifique o nome do arquivo de saída. Se nada for especificado, a saída padrão será
usava (-c, -h, -C e -S apenas modos). Observe que se um arquivo de saída for especificado em um
opção de arquivo de múltiplas saídas (como o padrão ou com a opção -r), então o arquivo de saída
substitui o nome gerado por padrão (que é baseado no nome principal da configuração
nome do arquivo).
A -s, -l e -m as opções estão presentes apenas para rpcgen Apoio, suporte. Ver rpcgen(1) para
informações sobre seu uso.
rxgen SINTAXE RESUMO
Arquivo de especificação:
|
|
|
|
|
:
"pacote"
:
"prefixo"
:
"código inicial"
:
"prefixo dividido" ";"
:
"DENTRO=" "|"
"FORA=" "|"
:
["proc"] [ ] [ ]
["dividir" | "multi"]
["=" ] ";"
:
"(" ")"
:
["<" ">" | "[" "]"] | NULO
:
"," | NULO
:
"DENTRO" | "FORA" | "INOUT" | NULO
:
| NULO
:
:
:
:
:
:
:
:
:
:
:
Sintaxe da linguagem RPCL da Sun (consulte rpcgen(1))
rxgen COMANDOS
Comentários e Pré-processando
A interface de entrada pode conter diretivas de pré-processador que são passadas através do C
pré-processador (ou seja, "cpp"). Como o pré-processador é executado em todos os arquivos de entrada antes de serem
realmente interpretado por rxgen, todos cpp diretivas (#include, #ifdefs, #defines, etc.) são
legal e bem-vindo dentro de um rxgen Arquivo de entrada. Claro, nenhum desses pré-processadores
serão incluídas em qualquer um dos arquivos gerados. Para facilitar distinções
entre os diferentes tipos de arquivos de saída, rxgen define certos especiais cpp símbolos para
uso pelo rxgen programador. Estes são RPC_HDR (definidos ao compilar no cabeçalho,
nome do arquivo.h, arquivos), RPC_XDR (definido ao compilar em xdr, nome do arquivo.xdr.c, arquivos),
RPC_CLIENT (definido ao compilar em stubs de cliente, nome do arquivo.cs.c, arquivos) e
RPC_SERVER (definido ao compilar em stubs de servidor, nome do arquivo.ss.c, arquivos).
Além disso, rxgen faz um pequeno pré-processamento próprio. Qualquer linha que comece com "%" é
passado diretamente para o arquivo de saída, não interpretado por rxgen. Para um en masse mais pesado
dumping de código não interpretado, seria aconselhável incluir todo esse código em um
arquivo "#include" e passe-o precedido de "%". A interface de entrada também pode conter qualquer
Comentários no estilo C que são, obviamente, ignorados. A interpretação é baseada em tokens, portanto
orientação de linha especial de instruções separadas não é necessária. rxgen Também fornece uma
conjunto bastante rico e útil de relatórios de erros, identificando-os pela localização exata da linha e
tipo de erro. Também, rxgen irá gerar automaticamente linhas #include para inclusão padrão
arquivos, como rx/xdr.h e rx/rx.h, junto com o arquivo de cabeçalho gerado a partir deste
interface.
Prefixando toco procedimentos
A pacote declaração diz rxgen o nome do pacote de interface. É usado para
prefixando a nomenclatura de todas as rotinas stub geradas e o procedimento de solicitação de execução.
Por exemplo:
pacote AFS_
faz com que o procedimento de solicitação de execução seja denominado AFS_ExecuteRequest (Aviso: no antigo
versão, um "_" adicional foi anexado após o nome do pacote ao nome ExecuteRequest;
portanto, certifique-se de não ter uma rotina de interface ExecuteRequest) e um determinado stub
rotina, digamos Fetch, seja realmente chamada de AFS_Fetch. Várias instruções de pacote (atual
o tamanho máximo é 10) por configuração são permitidos e são úteis quando vários conjuntos de
interfaces são implementadas (veja o exemplo no final). Observe que, nesses casos, o uso de
da -p flag resulta na geração de apenas um procedimento ExecuteRequest que
reconhece as múltiplas interfaces e cujo nome é prefixado pelo primeiro pacote
declaração. No caso padrão, procedimentos ExecuteRequest independentes serão criados para
cada grupo empacotado de chamadas de procedimento remoto.
A prefixo fornece um nome para preceder todas as chamadas para nomes de procedimentos remotos em
a rotina de stub ExecuteRequest. É útil quando o servidor faz chamadas RPC para outros
servidores (digamos, para fins de depuração). Por exemplo:
prefixo S
faz com que o nome "S" seja anexado ao nome de todas as rotinas chamadas do servidor
tocos. O servidor pode então chamar o nome original e obter os stubs do cliente.
rxgen procedimentos declaração
A proc declaração é a mais comum (e significativa) no rxgen interface. Sua sintaxe
a descrição é:
[proc] [ ] [ ] ( , ..., )
[dividir | multi] [= ] ;
em que:
· "proc" é um prefixo opcional da instrução do procedimento. Este é apenas um item estilístico
e não um delimitador de procedimento obrigatório.
· é o nome do procedimento. Observe que até o nome do procedimento é
opcional. Isso só faz sentido quando o nome do procedimento fornecido é idêntico ao
o nome do último pacote declaração (ou seja, "pacote RCallBack" e a declaração de
procedimento "RCallBack").
· , se presente, faz com que o procedimento ExecuteRequest chame esse stub
do stub gerado automaticamente quando uma chamada com esse opcode é decodificada.
· é uma constante ou símbolo que é o código de operação desse procedimento. Alguém pode usar
os recursos do pré-processador (ou seja, #define), o const Recurso de linguagem RPC, ou o antigo
boas constantes como opcodes. É feita alguma avaliação/processamento adicional de opcodes.
Particularmente, são realizadas verificações de opcodes duplicados e inexistentes, juntamente com
verifica "buracos" (ou seja, lacunas em opcodes consecutivos) nas sequências de opcode. Para
Por exemplo, usamos o fato de que quando existem "buracos" nos opcodes, o ExecuteRequest
procedimento usa o casas declaração em vez da indexação mais rápida (e menor, em código)
método de matriz.
Também, rxgen define (ou seja, anexa ao arquivo de cabeçalho) três macros valiosas para cada
grupo de pacotes: MENOR_OPCODE, HIGHEST_OPCODE e
NUMBER_OPCODES. Estes podem ser úteis para o rxgen programador. Também,
observe que o opcode instrução é um recurso opcional e pode ser omitido. Em tal
casos, os números de opcode automáticos são gerados sequencialmente, começando em 0.
Pode-se alterar o número do opcode inicial usando o código de inicialização (por falta de
nome melhor) rxgen comando. Sua sintaxe é:
código de inicialização
onde deve ser razoável! Observe que não se pode misturar procedimentos, alguns com
opcodes e alguns sem, nem permitem opcodes após a especificação do
código de inicialização declaração. rxgen reclamará em todos esses casos.
· O argumento entrada representa um determinado parâmetro do procedimento. Sua sintaxe é:
[DENTRO | INOUT | FORA | ]
[ |<>|[máx.]|[]]
Se o tipo for indireto (ou seja, for seguido por *), assume-se que o ponteiro
deve ser seguido um nível e os dados apontados devem ser transmitidos. Isto deveria
normalmente ser usado para todas as estruturas/matrizes e parâmetros de saída. Uma exceção notável
é quando o tamanho máximo explícito da matriz/estrutura é fornecido; já que não há matriz de ponteiro
declarações são permitidas, deve-se usar typedefs para obter o efeito semelhante. O
parâmetros podem ser parâmetros de entrada (precedidos por IN), parâmetros de saída (precedidos por
OUT) ou parâmetros de entrada/saída (precedidos por INOUT). Se não for especificado, então o
a direção do parâmetro anterior no procedimento é usada. (Nota: o primeiro
parâmetro deve ser precedido pela primitiva direcional!)
· "split" é um hack para lidar com rotinas stub que fazem coisas como transferências de arquivos ou qualquer
outra operação que tem que trocar informações (por exemplo, comprimento de um arquivo) antes do
call retorna seus parâmetros de saída. Por causa do aperto de mão específico que é
envolvidas na transferência remota de arquivos, atualmente dividimos todas essas chamadas em duas
rotinas stub do lado do cliente. O primeiro (com o prefixo padrão "Begin") é usado para
passe todos os parâmetros IN e INOUT para o lado do servidor. O segundo (com o padrão
prefixo de "End") é usado para recuperar os parâmetros INOUT e OUT do servidor.
Entre as duas chamadas, o usuário deve fazer as chamadas apropriadas para o arquivo
transferir. Por exemplo, a seguinte declaração de procedimento no pacote AFS_
Buscar (IN a, b,INOUT c, OUT d) divisão = FETCHOPCODE;
gerará aproximadamente as duas rotinas de stub de cliente independentes:
BeginAFS_Fetch (IN a, b, c)
e
FimAFS_Fetch(OUT c, d)
A prefixo dividido instrução é usada para alterar os nomes de prefixo padrão usados pelos dois
Rotinas geradas por stub do lado do cliente ao lidar com procedimentos relacionados à transferência de arquivos
chamadas. Por exemplo:
splitprefix IN = Antes_ OUT = Depois_
causará a nomeação dos dois stubs de cliente para uma rotina relacionada à transferência de arquivos, digamos
Buscar(), ser estar Antes_AFS_Fetch() e Depois_AFS_Fetch(), Respectivamente.
· A opção "multi" é quase idêntica ao recurso "split" descrito acima. A única
diferença visível significativa é que junto com os dois stubs de cliente, o padrão
stub do cliente também é gerado. Como a intenção é atender chamadas multi-Rx,
precisa de todo o esboço do procedimento padrão nos casos em que nenhuma chamada multi-Rx do
procedimento é realizado. Um efeito colateral da opção "multi" é a geração de um
macro especial (ou seja, "multi_ " que devolve como argumentos o "Begin"
e stubs "End" no arquivo de saída do cabeçalho. Esta macro é usada diretamente pelo código Rx
quando uma chamada multi-Rx deste procedimento é executada.
OBSOLETO rxgen CARATERÍSTICAS
Embora os seguintes comandos rxgen ainda estejam em vigor, eles serão removidos em breve, pois
existem alternativas melhores. NÃO OS USE!
A especial declaração é um hack temporário usado para lidar com certas ineficiências de
rotinas xdr padrão para lidar com algumas declarações personalizadas pelo usuário. Em particular, este
aplica-se a um ponteiro de string especificado como parte de uma declaração. Por exemplo,
estrutura especial BBS SeqBody;
conta rxgen que a entrada "SeqBody" na rotina BBS xdr definida pelo usuário é uma string (observe
que mais de uma string pode ser "especial" por estrutura - múltiplas são separadas por
vírgulas); assim, ele alocará e desalocará espaço adequadamente no servidor gerado
stubs que contêm essa estrutura como um parâmetro IN ou INOUT.
Uma alternativa melhor para especial é o personalizado afirmação, que é simplesmente a
token "customizado" seguido pela declaração regular de uma estrutura baseada no RPCL
regras. Neste caso, a declaração será incluída no arquivo de cabeçalho gerado (-h
opção), mas nenhuma rotina xdr será gerada para esta estrutura - o usuário fornecerá
esse. Todas as entradas de ponteiro nesta estrutura serão lembradas quando a estrutura for
usado como IN ou INOUT no stub do servidor, nenhum vazamento de núcleo ocorrerá. Por exemplo,
considerar
estrutura personalizada CBS {
sequência longa;
char *SeqBody;
}
A rotina "xdr_CBS" seria fornecida pelo usuário onde durante o opcode DECODE xdr,
espaço apropriado para a string "SeqBody" é alocado. Da mesma forma, esse espaço é liberado
durante o opcode xdr GRATUITO.
Nota: O estilo antigo de "Especificações de parâmetros de array" não é mais suportado.
EXEMPLOS
Caso existam alguns requisitos não disponíveis na linguagem RPC atual, pode-se
personalize algumas rotinas XDR deixando esses tipos de dados indefinidos. Para cada tipo de dados
que é indefinido, será assumido que existe uma rotina com o nome "xdr_" prefixado
para isso. Um conjunto selecionado de rxgen recursos são apresentados abaixo, mas para uma visão mais abrangente
um (sindicatos, exemplos complexos, etc), consulte o rpcgen Programação Guia e
externo Data Representação: Espreguiçadeiras Técnicos Notas.
typedefs
A instrução typedef RPC é idêntica à typedef C (ou seja, "typedef ").
Por padrão, a maioria das declarações de usuário (isto é, structs, unions, etc.) são automaticamente
digitado por rxgen. Por tornar a análise mais simples, seu uso é recomendado por rxgen
scripts.
Cordas
A convenção de string C "char *" é meio ambígua, pois geralmente tem como objetivo
significa uma sequência de caracteres terminada em nulo, mas também pode representar um ponteiro para um
único caractere, um ponteiro para uma matriz de caracteres, etc. Na linguagem RPC, um valor nulo
string terminada é inequivocamente chamada de "string". Exemplos,
string nome grande<>;
nome da sequência ;
typedef string volnome ;
Observe que o tamanho máximo da string pode ser arbitrário (como "bigname" acima) ou,
de preferência, ou especificado entre colchetes angulares (ou seja, "nome" e "volname" acima). Em
Na prática, deve-se sempre usar apenas strings limitadas em interfaces. Um exemplo de procedimento de chamada
usando as declarações acima seria:
GetEntryByName (IN nome do vol,
OUT estrutura vldbentry *entrada) = VL_GETENTRYBYNAME;
ou, claro,
GetEntryByName (IN string volname ,
OUT estrutura vldbentry *entrada) = VL_GETENTRYBYNAME;
É muito importante que o usuário entenda quando os parâmetros da string devem ser
alocado e/ou liberado pelos seus programas cliente e/ou servidor. Uma breve análise sobre
O tratamento de parâmetros de string segue (observe que um método semelhante é usado para o tratamento de
matrizes de comprimento variável, como será mostrado mais adiante):
· No lado do cliente: os parâmetros das strings IN e INOUT são de responsabilidade do programador
e deve ser alocado (estático ou via malloc) antes de chamar o rpc e liberado (se
foi utilizado malloc) após o retorno do rpc no programa cliente do usuário; claro, para
Parâmetros INOUT, a string retornada não pode ser maior que a string de entrada mallocada.
Os parâmetros da string OUT são automaticamente alocados (com base no comprimento do valor retornado
string e não o maxsize) pelo rxgen stubs de cliente (em nome do arquivo.cs.c) e deve ser
liberado pelo programa cliente; é certo que isso pode ser um pouco confuso, pois o usuário
precisa liberar algo que não alocou.}
· No lado do servidor: os parâmetros das strings IN e INOUT são automaticamente mallocados (com base em
o tamanho das strings de entrada) pelos stubs do servidor rxgen (em nome do arquivo.ss.c) antes que eles
são passados para o procedimento do servidor do usuário; esse espaço é liberado automaticamente pouco antes
o stub do servidor rxgen retorna; portanto, o usuário não precisa fazer nada de especial para IN
e parâmetros de string INOUT.
Os parâmetros da string OUT devem ser alocados pelo procedimento do servidor do usuário (ou seja, ponteiro nulo
é passado para ele pelo stub do servidor rxgen) e é automaticamente liberado no final do
da rxgen esboço do servidor. Como no lado do cliente, os parâmetros OUT são um tanto
pouco ortodoxo (ou seja, a rotina do servidor deve malloc uma string sem nunca liberá-la;
isso é feito pelo rxgen stub do servidor).
Observe que para os parâmetros de string INOUT e OUT, tanto no lado do cliente quanto no lado do servidor, seus
os argumentos devem ser char de ponteiros (ou seja, char **).
Ponteiros
As declarações de ponteiro em RPC também são exatamente como em C (ou seja, "struct
single_vldbentry *vldblist;"). É claro que não é possível enviar ponteiros pela rede, mas
pode-se usar ponteiros XDR para enviar tipos de dados recursivos, como listas e árvores (um
exemplo de lista vinculada será demonstrado em breve).
Arrays
Matrizes fixas são como declarações de matriz C padrão (ou seja, "struct UpdateEntry
entradas[20]") sem quaisquer problemas de efeitos colaterais em rxgen. Como as matrizes de comprimento variável têm
nenhuma sintaxe explícita em C, os colchetes angulares são usados para isso e as declarações de array são
na verdade compilado em "struct"s. Por exemplo, declarações como:
const MAXBULKSIZE = 10000;
const MAXENTRIAS = 100;
volume opaco ; /* No máximo 10000 itens */
hosts internos<>; /* qualquer número de itens */
typedef vldbentry blkentries<100>; /* Decl de array preferível */
são compilados nas seguintes estruturas:
estrutura {
u_int bulk_len; /* número de itens */
char *bulk_val; /* ponteiro para array */
} volume;
para a matriz "bulk" e da mesma forma para a matriz "blkentries<100>",
estrutura {
u_int blkentries_len; /* número de itens no array */
vldbentry *blkentries_val; /* ponteiro para array */
} entradas em branco;
Portanto, o usuário deve estar ciente das entradas de estrutura geradas "magicamente", como
o número de itens na matriz ( _len) e o ponteiro para o array
( _val), pois algumas das entradas deverão ser preenchidas a partir do
programas cliente/servidor. Um exemplo de processo seria:
typedef vldbentry blkentries ;
proc GetBlk (OUT blkentries *vlentries) = VL_GETBLK;
ou, mais diretamente,
GetBlk(OUT vldbentry vlentries ) = VL_GETBLK;
Observe que embora o método mais recente seja preferível, já que não é necessário usar primeiro
a instrução typedef (e reconhecidamente, os programadores preferem evitar typedefs), deve-se
perceber que rxgen faz a expansão da estrutura e a criação do xdr implicitamente; portanto
o usuário deve estar ciente dos campos "vldbentries_val" e "vldbentries_len" como antes
(veja os exemplos a seguir).
Ordem exemplo I (ao menos desejável)
Declaração de procedimento na configuração da interface:
proc ListAttributes (IN vldblistbyattributes *atributos,
INOUT blkentries *vldbentries) = VL_LISTATTRIBUTES;
Exemplo de código CLIENTE:
entradas blkentries, *pnt;
entradas.blkentries_len = 10; /* max # entradas retornadas */
entradas.blkentries_val = (vldbentry *)malloc(LEN);
/* Deve ser definido */
código = VL_ListAttributes(&atributos, &entradas);
if (!código){
pnt = entradas.blkentries_val;
para (i=0; i <entradas.blkentries_len; i++, pnt++)
display_vldbentry(pnt);
/* Certifique-se de liberar o espaço alocado */
free((char *)entries.blkentries_val);
}
Exemplo de código SERVIDOR:
VL_ListAttributes(atributos, entradas)
{
vldbentry *singleentry = entradas->blkentries_val;
entradas->blkentries_len = 0;
enquanto (copy_to_vldbentry(&vlentry, entrada única))
entrada única++, vldbentries->entries_len++;
}
Embora esse método para matrizes de tamanho variável funcione bem, existem algumas desvantagens importantes.
O parâmetro array (ou seja, vldbentries acima) deve ser declarado como INOUT, pois precisamos
passe o comprimento máximo do array retornado esperado; mais importante ainda, um grande (dependendo
o valor de "_len") um pedaço de código indesejado será transferido para o servidor como resultado
do efeito colateral IN(out) da matriz. É um método fácil e conveniente se o
o tamanho do array retornado pode ser previsto desde o início e quando o tamanho for bastante alto. Esse
método é incluído como um exemplo de uso errôneo (e abuso) de rxgen e não deveria ser
usava.
Ordem exemplo II (Desejável método)
Declaração de procedimento na configuração da interface (usando o Exemplo I acima):
proc ListAttributes (IN vldblistbyattributes *atributos,
OUT blkentries *vldbentries) = VL_LISTATTRIBUTES;
Exemplo de código CLIENTE:
entradas blkentries, *pnt;
código = VL_ListAttributes(&atributos, &entradas);
if (!código){
pnt = entradas.blkentries_val;
para (i=0; i <entradas.blkentries_len; i++, pnt++)
display_vldbentry(pnt);
/* Certifique-se de liberar o espaço alocado (por rxgen) */
free((char *)entries.blkentries_val);
}
Exemplo de código SERVIDOR:
VL_ListAttributes(atributos, entradas)
{
vldbentry *entrada única;
entradas->blkentries_len = 0;
entrada única = entradas->blkentries_val
= (vldbentry *)malloc(MAXENTRIES * sizeof(vldbentry));
enquanto (copy_to_vldbentry(&vlentry, entrada única))
entrada única++, vldbentries->entries_len++;
}
Esta é a melhor (e mais simples) maneira de usar arrays de tamanho variável como parâmetro de saída.
É responsabilidade do stub do lado do servidor malloc () o espaço adequado que é
liberado automaticamente pelo rxgen esboço; o lado do cliente deve liberar o espaço alocado por
da rxgen- esboço de chamada.
Ordem exemplo III (Vinculado Listas)
Considerando as 3 declarações a seguir (poderiam ter aplicado algumas otimizações) no
arquivo de configuração:
typedef struct single_vldbentry *vldblist;
estrutura single_vldbentry {
vldbentry vlentry;
vldblist next_vldb;
};
estrutura vldb_list {
nó vldblist;
};
e a declaração do procedimento rxgen:
LinkedList (IN vldblistbyattributes *atributos,
OUT vldb_list *linkedentries) = VL_LINKEDLIST;
Exemplo de código CLIENTE:
vldb_list vinculadovldbs;
vldblist vllist, vllist1;
bzero(&linkedvldbs, sizeof(vldb_list));
código = VL_LinkedList(&atributos, &nentries, &linkedvldbs);
if (!código){
printf("Recebemos %d entradas vldb\n", nentries);
for (vllist = linkedvldbs.node; vllist; vllist = vllist1) {
listav1 = listav->próximo_vldb;
display_entry(&vllist->vlentry);
free((char *)vllist);
}
}
Exemplo de código SERVIDOR:
VL_LinkedList(rxcall, atributos, nentries, linkedvldbs);
{
vldblist vllist, *vllistptr = &linkedvldbs->node;
enquanto (...) {
listav = *listavlptr
= (single_vldbentry *)malloc (sizeof (single_vldbentry));
copy_to_vldbentry(&tentry, &vllist->vlentry);
entradas++;
vllistptr = &vllist->next_vldb;
};
*vllistptr = NULO;
}
Usar uma lista vinculada oferece muitas vantagens: Nada é passado para o servidor (o parâmetro
está OUT), nenhuma sobrecarga adicional está envolvida e o chamador não precisa explicitamente
prepare-se para um tamanho de retorno arbitrário. Uma desvantagem é que o chamador tem o
responsabilidade de malloc () (no servidor) e gratuito (no cliente) de cada entrada (para
evitar vazamentos indesejados no núcleo). Outra desvantagem é que, por ser uma chamada recursiva, o C
pilha crescerá linearmente em relação ao número de nós na lista (por isso é aconselhável
aumentar a pilha Rx LWP se grandes quantidades de dados forem esperadas de volta - tamanho padrão da pilha
é 4K). As vantagens devem superar as desvantagens aqui.
É importante prestar atenção aos comentários dos três exemplos de array acima
particularmente quando são referências a quando o usuário deve alocar/liberar espaço para o
matrizes de comprimento variável. O mecanismo é muito semelhante ao manuseio de strings, portanto você
pode ser necessário revisar a seção de strings acima; observe que as listas vinculadas são tratadas
um pouco diferente...
Gerais exemplos
Abaixo está uma versão abreviada de um arquivo de interface aleatório que mostra alguns dos
casos.
/* Declaração de todas as estruturas utilizadas pela interface do script R.xg */
estrutura AFSFid {
Volume longo não assinado;
Vnode longo não assinado;
não assinado longo Único;
};
typedef longo ViceDataType;
/* Observe que TEST seria equivalente a "HEADER" apenas durante o
processamento do cabeçalho, *.h, arquivo */
#ifdefRPC_HDR
#define TESTE "CABEÇALHO"
#else
#define TESTE "REST"
#fim se
/* Este é o arquivo de especificação padrão *.xg */
pacote AFS_
splitprefix IN = ANTES_ OUT = DEPOIS_;
Teste de prefixo
proc Remove(IN estrutura AFSFid *Did, IN string volname<64>,
Estrutura OUT AFSStatus *Status) = AFS_REMOVE;
DesconectarFS AUX_disconnectFS() = AFS_DISCONNECTFS;
proc GetVolumeInfo(IN string Vid,
OUT estrutura VolumeInfo *Info) = AFS_GETVOLUMEINFO;
/* Você poderia ter mais de uma interface por configuração */
pacote VOTE_
/* Usando o recurso "multi"; portanto, VOTE_Beacon pode ser chamado como um
chamada multi-Rx ou como uma chamada normal */
Beacon (EM estado longo, voteStart longo,
net_version *versão, net_tid *tid)
multi=VOTE_BEACON;
pacote DISK_
/* Usando o recurso "dividir" */
SendFile (IN arquivo longo, deslocamento longo,
comprimento longo, net_version *versão)
divisão = DISK_SENDFILE;
saída of an real interface • Configuração
Demonstraremos alguns dos resultados reais gerados por rxgen seguindo uma abreviação
configuração real da interface.
Configuração lima
Conteúdo do arquivo de configuração da interface (vldbint.xg):
pacote VL_
#include "vl_opcodes.h" /* Os opcodes estão incluídos aqui */
%#include "vl_opcodes.h" /* diretamente para outros lugares */
/* Limitações atuais em parâmetros que afetam outros pacotes
(ou seja, volume) */
const MAXNAMELEN = 65;
const MAXNSERVERS = 8;
const MAXTYPES = 3;
/* Representação externa (visível) de uma entrada vldb individual */
estrutura vldbentry {
nome do caracter[MAXNAMELEN];
volumeType longo;
nServers longos;
número_servidor longo[MAXNSERVERS];
longa serverPartition[MAXNSERVERS];
serverFlags longos[MAXNSERVERS];
u_long volumeId[MAXTYPES];
bandeiras longas;
};
typedef struct single_vldbentry *vldblist;
estrutura single_vldbentry {
vldbentry VldbEntry;
vldblist next_vldb;
};
estrutura vldb_list {
nó vldblist;
};
/* chamadas de interface vldb */
CreateEntry (em Volid longo,
vldbentry *novaentrada) = VLCREATEENTRY;
GetEntryByName (string IN nome do volume ,
OUT vldbentry *entrada) = VLGETENTRYBYNAME;
GetNewVolumeId (em contagem longa,
OUT longo *novovolumid) = VLGETNEWVOLUMEID;
SubstituirEntry (em Volid longo,
voltipo longo,
vldbentry *novaentrada,
ReleaseType longo) multi = VLREPLACEENTRY;
ListAttributes (IN VldbListByAttributes *atributos,
OUT entradas longas *,
OUT vldbentry bulkentries )
= VLLISTATTRIBUTOS;
LinkedList (IN VldbListByAttributes *atributos,
OUT entradas longas *,
OUT vldb_list *linkedentries) = VLLINKEDLIST;
Vamos nos concentrar apenas no código gerado por Rx, já que o código gerado por R (-R opção)
em breve ficará obsoleto. Para uma descrição detalhada das chamadas relacionadas a Rx dentro do
stubs gerados (ou seja, rx_NewCall(), rx_EndCall()), juntamente com detalhes sobre o que acontece
dentro de certas chamadas (como xdrrx_create()) consulte a documentação Rx. Digitando
"rxgen vldbint.xg" resultará na criação de quatro arquivos: vldbint.h, vldbint.xdr.c,
vldbint.cs.c e vldbint.ss.c. A seguir, uma análise mais detalhada desses arquivos.
cabeçalho lima (vldbint.h)
/* Arquivo gerado por máquina -- NÃO edite */
#include "vl_opcodes.h" /* diretamente para outros lugares */
#define MAXNAMELEN 65
#define MAXNSERVERS 8
#define MAXTYPES 3
estrutura vldbentry {
nome do caracter[MAXNAMELEN];
volumeType longo;
nServers longos;
número_servidor longo[MAXNSERVERS];
longa serverPartition[MAXNSERVERS];
serverFlags longos[MAXNSERVERS];
u_long volumeId[MAXTYPES];
bandeiras longas;
};
typedef struct vldbentry vldbentry;
bool_t xdr_vldbentry();
typedef struct single_vldbentry *vldblist;
bool_t xdr_vldblist();
estrutura single_vldbentry {
vldbentry VldbEntry;
vldblist next_vldb;
};
typedef struct single_vldbentry single_vldbentry;
bool_t xdr_single_vldbentry();
estrutura vldb_list {
nó vldblist;
};
typedef struct vldb_list vldb_list;
bool_t xdr_vldb_list();
#incluir
#define multi_VL_ReplaceEntry(Volid, voltype, newentry, ReleaseType) \
multi_Body(StartVL_ReplaceEntry(multi_call, Volid, voltype,
nova entrada, ReleaseType), EndVL_ReplaceEntry(multi_chamada))
typedef struct bulkentries {
u_int bulkentries_len;
vldbentry *bulkentries_val;
} entradas em massa;
bool_t xdr_bulkentries();
/* Estatísticas úteis relacionadas ao Opcode para o pacote: VL_ */
#define VL_LOWEST_OPCODE 501
#define VL_HIGHEST_OPCODE 506
#define VL_NUMBER_OPCODES 6
Observe que todas as estruturas são digitadas automaticamente e todos os "const"s são convertidos para
"#definir". Algumas estruturas de dados, como entradas em massa, são obtidas de parâmetros de procedimento
(do processo ListAttributes). Portanto, isso deve ser mantido em mente ao criar stubs
aos poucos com rxgen (ou seja, usando o -c, -h, -Cou -S bandeiras). Além disso, um dos lados
efeitos da opção "multi" (no proc "ReplaceEntry") é a geração do
"multi_VL_ReplaceEntry" acima.
XDR rotinas for estruturas (vldbint.xdr.c)
/* Arquivo gerado por máquina -- NÃO edite */
#incluir
#include "vldbint.h"
#include "vl_opcodes.h" /* diretamente para outros lugares */
bool_t
xdr_vldbentry(xdrs, objp)
XDR *xdrs;
vldbentry *objp;
{
if (!xdr_vector(xdrs, (char *)objp->nome, MAXNAMELEN,
sizeof(char), xdr_char))
retorna falso);
if (!xdr_long(xdrs, &objp->volumeType))
retorna falso);
if (!xdr_long(xdrs, &objp->nServers))
retorna falso);
if (!xdr_vector(xdrs, (char *)objp->serverNumber, MAXNSERVERS,
sizeof(longo), xdr_long))
retorna falso);
if (!xdr_vector(xdrs, (char *)objp->serverPartition,
MAXNSERVERS, sizeof(longo), xdr_long))
retorna falso);
if (!xdr_vector(xdrs, (char *)objp->serverFlags, MAXNSERVERS,
sizeof(longo), xdr_long))
retorna falso);
if (!xdr_vector(xdrs, (char *)objp->volumeId, MAXTYPES,
sizeof(u_long), xdr_u_long))
retorna falso);
if (!xdr_long(xdrs, &objp->flags))
retorna falso);
retornar (VERDADEIRO);
}
bool_t
xdr_vldblist(xdrs, objp)
XDR *xdrs;
vldblist *objp;
{
if (!xdr_pointer(xdrs, (char **)objp,
sizeof(estrutura single_vldbentry),
xdr_single_vldbentry))
retorna falso);
retornar (VERDADEIRO);
}
bool_t
xdr_single_vldbentry(xdrs,objp)
XDR *xdrs;
single_vldbentry *objp;
{
if (!xdr_vldbentry(xdrs, &objp->VldbEntry))
retorna falso);
if (!xdr_vldblist(xdrs, &objp->next_vldb))
retorna falso);
retornar (VERDADEIRO);
}
bool_t
xdr_vldb_list(xdrs,objp)
XDR *xdrs;
vldb_list *objp;
{
if (!xdr_vldblist(xdrs, &objp->nó))
retorna falso);
retornar (VERDADEIRO);
}
bool_t
xdr_bulkentries(xdrs, objp)
XDR *xdrs;
entradas em massa *objp;
{
if (!xdr_array(xdrs, (char **)&objp->bulkentries_val,
(u_int *)&objp->bulkentries_len, MAXVLDBLEN,
sizeof(vldbentry), xdr_vldbentry))
retorna falso);
retornar (VERDADEIRO);
}
Observe que o xdr_bulkentries() é gerado automaticamente como efeito colateral de um procedimento
declaração de parâmetro. Assim, se forem usadas declarações idênticas de parâmetros de vários tipos,
então stubs xdr_* definidos por multiplicação serão criados! Sentimos que esta era uma alternativa melhor
para ter o rxgen programador lida com tipos como bulkentries_1, bulkentries_2...
Cliente toco rotinas (vldbint.cs.c)
/* Arquivo gerado por máquina -- NÃO edite */
#incluir
#incluir
#incluir
#include "vldbint.h"
#include "vl_opcodes.h" /* diretamente para outros lugares */
int VL_CreateEntry(z_conn, Volid, nova entrada)
registrar estrutura rx_connection *z_conn;
longo Volid;
vldbentry * nova entrada;
{
estrutura rx_call *z_call = rx_NewCall(z_conn);
estático int z_op = 501;
int z_resultado;
XDR z_xdrs;
xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
/* Organiza os argumentos */
se ((!xdr_int(&z_xdrs, &z_op))
|| (!xdr_long(&z_xdrs, &Vólido))
|| (!xdr_vldbentry(&z_xdrs, nova entrada))) {
z_result = RXGEN_CC_MARSHAL;
vou falhar;
}
z_result = RXGEN_SUCCESS;
falhou:
retornar rx_EndCall(z_call, z_result);
}
int VL_GetEntryByName(z_conn, nome do volume, entrada)
registrar estrutura rx_connection *z_conn;
char * nome do volume;
vldbentry*entrada;
{
estrutura rx_call *z_call = rx_NewCall(z_conn);
estático int z_op = 504;
int z_resultado;
XDR z_xdrs;
xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
/* Organiza os argumentos */
se ((!xdr_int(&z_xdrs, &z_op))
|| (!xdr_string(&z_xdrs, &nomedovolume, 65))) {
z_result = RXGEN_CC_MARSHAL;
vou falhar;
}
/* Desempacotar os argumentos de resposta */
z_xdrs.x_op = XDR_DECODE;
if ((!xdr_vldbentry(&z_xdrs, entrada))) {
z_result = RXGEN_CC_UNMARSHAL;
vou falhar;
}
z_result = RXGEN_SUCCESS;
falhou:
retornar rx_EndCall(z_call, z_result);
}
int VL_GetNewVolumeId(z_conn, bumpcount, novo volume)
registrar estrutura rx_connection *z_conn;
contagem longa;
longo * novo volume;
{
estrutura rx_call *z_call = rx_NewCall(z_conn);
estático int z_op = 505;
int z_resultado;
XDR z_xdrs;
xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
/* Organiza os argumentos */
se ((!xdr_int(&z_xdrs, &z_op))
|| (!xdr_long(&z_xdrs, &bumpcount))) {
z_result = RXGEN_CC_MARSHAL;
vou falhar;
}
/* Desempacotar os argumentos de resposta */
z_xdrs.x_op = XDR_DECODE;
if ((!xdr_long(&z_xdrs, novovolumid))) {
z_result = RXGEN_CC_UNMARSHAL;
vou falhar;
}
z_result = RXGEN_SUCCESS;
falhou:
retornar rx_EndCall(z_call, z_result);
}
int VL_ReplaceEntry(z_conn, Volid, voltype, nova entrada, ReleaseType)
registrar estrutura rx_connection *z_conn;
Volid longo, voltype, ReleaseType;
vldbentry * nova entrada;
{
estrutura rx_call *z_call = rx_NewCall(z_conn);
estático int z_op = 506;
int z_resultado;
XDR z_xdrs;
xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
/* Organiza os argumentos */
se ((!xdr_int(&z_xdrs, &z_op))
|| (!xdr_long(&z_xdrs, &Vólido))
|| (!xdr_long(&z_xdrs, &voltype))
|| (!xdr_vldbentry(&z_xdrs, nova entrada))
|| (!xdr_long(&z_xdrs, &ReleaseType))) {
z_result = RXGEN_CC_MARSHAL;
vou falhar;
}
z_result = RXGEN_SUCCESS;
falhou:
retornar rx_EndCall(z_call, z_result);
}
int StartVL_ReplaceEntry(z_call, Volid, voltype, nova entrada, ReleaseType)
registrar estrutura rx_call *z_call;
Volid longo, voltype, ReleaseType;
vldbentry * nova entrada;
{
estático int z_op = 506;
int z_resultado;
XDR z_xdrs;
xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
/* Organiza os argumentos */
se ((!xdr_int(&z_xdrs, &z_op))
|| (!xdr_long(&z_xdrs, &Vólido))
|| (!xdr_long(&z_xdrs, &voltype))
|| (!xdr_vldbentry(&z_xdrs, nova entrada))
|| (!xdr_long(&z_xdrs, &ReleaseType))) {
z_result = RXGEN_CC_MARSHAL;
vou falhar;
}
z_result = RXGEN_SUCCESS;
falhou:
retornar z_resultado;
}
int EndVL_ReplaceEntry(z_call)
registrar estrutura rx_call *z_call;
{
int z_resultado;
XDR z_xdrs;
z_result = RXGEN_SUCCESS;
falhou:
retornar z_resultado;
}
int VL_ListAttributes(z_conn, atributos, nentries, bulkentries_1)
registrar estrutura rx_connection *z_conn;
VldbListByAttributes * atributos;
entradas longas *;
entradas em massa * entradas em massa_1;
{
estrutura rx_call *z_call = rx_NewCall(z_conn);
estático int z_op = 511;
int z_resultado;
XDR z_xdrs;
xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
/* Organiza os argumentos */
se ((!xdr_int(&z_xdrs, &z_op))
|| (!xdr_VldbListByAttributes(&z_xdrs, atributos))) {
z_result = RXGEN_CC_MARSHAL;
vou falhar;
}
/* Desempacotar os argumentos de resposta */
z_xdrs.x_op = XDR_DECODE;
if ((!xdr_long(&z_xdrs, nentries))
|| (!xdr_bulkentries(&z_xdrs, bulkentries_1))) {
z_result = RXGEN_CC_UNMARSHAL;
vou falhar;
}
z_result = RXGEN_SUCCESS;
falhou:
retornar rx_EndCall(z_call, z_result);
}
int VL_LinkedList(z_conn, atributos, nentries, linkedentries)
registrar estrutura rx_connection *z_conn;
VldbListByAttributes * atributos;
entradas longas *;
vldb_list * entradas vinculadas;
{
estrutura rx_call *z_call = rx_NewCall(z_conn);
estático int z_op = 512;
int z_resultado;
XDR z_xdrs;
xdrrx_create(&z_xdrs, z_call, XDR_ENCODE);
/* Organiza os argumentos */
se ((!xdr_int(&z_xdrs, &z_op))
|| (!xdr_VldbListByAttributes(&z_xdrs, atributos))) {
z_result = RXGEN_CC_MARSHAL;
vou falhar;
}
/* Desempacotar os argumentos de resposta */
z_xdrs.x_op = XDR_DECODE;
if ((!xdr_long(&z_xdrs, nentries))
|| (!xdr_vldb_list(&z_xdrs, linkedentries))) {
z_result = RXGEN_CC_UNMARSHAL;
vou falhar;
}
z_result = RXGEN_SUCCESS;
falhou:
retornar rx_EndCall(z_call, z_result);
}
Observe o efeito colateral do recurso "multi" (três módulos diferentes para "ReplaceEntry"
processo).
Do lado do servidor toco rotinas (vldbint.ss.c)
/* Arquivo gerado por máquina -- NÃO edite */
#incluir
#incluir
#incluir
#include "vldbint.h"
#include "vl_opcodes.h" /* diretamente para outros lugares */
_VL_CreateEntry longo (z_call, z_xdrs)
estrutura rx_call *z_call;
XDR *z_xdrs;
{
longo z_result;
longo Volid;
vldbentry nova entrada;
if ((!xdr_long(z_xdrs, &Volid))
|| (!xdr_vldbentry(z_xdrs, &newentry))) {
z_result = RXGEN_SS_UNMARSHAL;
vou falhar;
}
z_result = VL_CreateEntry(z_call, Volid, &newentry);
falhou:
retornar z_resultado;
}
longo _VL_GetEntryByName(z_call, z_xdrs)
estrutura rx_call *z_call;
XDR *z_xdrs;
{
longo z_result;
char *nome do volume = (char *)0;
entrada vldbentry;
if ((!xdr_string(z_xdrs, &nomedovolume, 65))) {
z_result = RXGEN_SS_UNMARSHAL;
vou falhar;
}
z_result = VL_GetEntryByName(z_call, &volumename, &entry);
z_xdrs->x_op = XDR_ENCODE;
if ((!xdr_vldbentry(z_xdrs, &entrada)))
z_result = RXGEN_SS_MARSHAL;
falhou:
z_xdrs->x_op = XDR_FREE;
if (!xdr_string(z_xdrs, &volumename, 65)) vá para fail1;
retornar z_resultado;
falha1:
retornar RXGEN_SS_XDRFREE;
}
longo _VL_GetNewVolumeId(z_call, z_xdrs)
estrutura rx_call *z_call;
XDR *z_xdrs;
{
longo z_result;
contagem longa;
longo novo volume;
if ((!xdr_long(z_xdrs, &bumpcount))) {
z_result = RXGEN_SS_UNMARSHAL;
vou falhar;
}
z_result = VL_GetNewVolumeId(z_call, bumpcount, &newvolumid);
z_xdrs->x_op = XDR_ENCODE;
if ((!xdr_long(z_xdrs, &newvolumid)))
z_result = RXGEN_SS_MARSHAL;
falhou:
retornar z_resultado;
}
_VL_ReplaceEntry longo(z_call, z_xdrs)
estrutura rx_call *z_call;
XDR *z_xdrs;
{
longo z_result;
Volid longo, voltype, ReleaseType;
vldbentry nova entrada;
if ((!xdr_long(z_xdrs, &Volid))
|| (!xdr_long(z_xdrs, &voltype))
|| (!xdr_vldbentry(z_xdrs, &newentry))
|| (!xdr_long(z_xdrs, &ReleaseType))) {
z_result = RXGEN_SS_UNMARSHAL;
vou falhar;
}
z_result = VL_ReplaceEntry(z_call, Volid, voltype, &newentry,
ReleaseType);
falhou:
retornar z_resultado;
}
_VL_ListAttributes longos(z_call, z_xdrs)
estrutura rx_call *z_call;
XDR *z_xdrs;
{
longo z_result;
Atributos VldbListByAttributes;
entradas longas;
entradas em massa bulkentries_1;
if ((!xdr_VldbListByAttributes(z_xdrs, &atributos))) {
z_result = RXGEN_SS_UNMARSHAL;
vou falhar;
}
z_result = VL_ListAttributes(z_call, &atributos, &nentries,
&bulkentries_1);
z_xdrs->x_op = XDR_ENCODE;
if ((!xdr_long(z_xdrs, &nentries))
|| (!xdr_bulkentries(z_xdrs, &bulkentries_1)))
z_result = RXGEN_SS_MARSHAL;
falhou:
z_xdrs->x_op = XDR_FREE;
if (!xdr_bulkentries(z_xdrs, &bulkentries_1)) vá para fail1;
retornar z_resultado;
falha1:
retornar RXGEN_SS_XDRFREE;
}
_VL_LinkedList longo (z_call, z_xdrs)
estrutura rx_call *z_call;
XDR *z_xdrs;
{
longo z_result;
Atributos VldbListByAttributes;
entradas longas;
vldb_list entradas vinculadas;
if ((!xdr_VldbListByAttributes(z_xdrs, &atributos))) {
z_result = RXGEN_SS_UNMARSHAL;
vou falhar;
}
z_result = VL_LinkedList(z_call, &atributos, &nentries,
&linkedentries);
z_xdrs->x_op = XDR_ENCODE;
if ((!xdr_long(z_xdrs, &nentries))
|| (!xdr_vldb_list(z_xdrs, &linkedentries)))
z_result = RXGEN_SS_MARSHAL;
falhou:
retornar z_resultado;
}
longo _VL_CreateEntry();
_VL_GetEntryByName longo();
longo _VL_GetNewVolumeId();
longo _VL_ReplaceEntry();
longo _VL_ListAttributes();
_VL_LinkedList longo();
estático longo (*StubProcsArray0[])() = {_VL_CreateEntry,
_VL_GetEntryByName, _VL_GetNewVolumeId, _VL_ReplaceEntry,
_VL_ListAttributes, _VL_LinkedList};
VL_ExecuteRequest(z_call)
registrar estrutura rx_call *z_call;
{
operação interna;
XDR z_xdrs;
longo z_result;
xdrrx_create(&z_xdrs, z_call, XDR_DECODE);
se (!xdr_int(&z_xdrs, &op))
z_resultado=RXGEN_DECODE;
senão if (op < VL_LOWEST_OPCODE || op > VL_HIGHEST_OPCODE)
z_resultado=RXGEN_OPCODE;
outro
z_result = (*StubProcsArray0[op - VL_LOWEST_OPCODE])
(z_call, &z_xdrs);
retornar z_resultado;
}
Se houvesse lacunas na sequência de opcode dos procedimentos, o código para VL_ExecuteRequest()
rotina teria sido drasticamente diferente (teria sido uma declaração de caso para
cada procedimento).
NOTAS
rxgen é implementado a partir do Sun rpcgen Utilitário. Todo o padrão rpcgen's
a funcionalidade é totalmente mantida. Observe que alguns ativos rpcgen opções que não se aplicam
para rxgenO propósito de não é mencionado aqui (ou seja, -s, -l, -m opções) e os interessados
leitor deve consultar rpcgen(1) para obter detalhes.
Quando o "%#include recurso é usado, certifique-se de que você não tenha nenhum
rxgen recursos de linguagem (ou seja, %#defines), pois você receberá erros de sintaxe durante
compilações..
Como este é um projeto em andamento, muitos dos itens acima podem mudar/desaparecer sem grande
Atenção.
Use rxgen online usando serviços onworks.net