<para>Um aspecto importante do &MCOP; é a <emphasis>linguagem de descrição de interfaces</emphasis>, ou &IDL;, na qual muitas das interfaces do &arts; e as <acronym>API</acronym>s estão definidas de forma independente da linguagem. </para>
<para>Para usar as interfaces de &IDL; a partir do C++, é compilada pelo compilador de &IDL; em código de C++. Quando você implementar uma interface, você irá derivar da classe de esqueleto que o compilador de &IDL; gerou. Quando usar uma interface, você irá fazer isso através de uma classe envolvente ('wrapper'). Desta forma, o &MCOP; pode usar um protocolo se o objecto com quem você está a falar não estiver local - você obtém assim a transparência de rede. </para>
<para>Este capítulo é suposto descrever as funcionalidades básicas do modelo de objectos que resulta da utilização do &MCOP;, o protocolo, como usar o &MCOP; no C++ (interface para a linguagem), e assim por diante. </para>
<para>Muitos dos serviços fornecidos pelo &arts;, como os módulos e o servidor de som, são definidos com base em <acronym>interfaces</acronym>. As interfaces são especificadas num formato independente de linguagens: a &IDL;. </para>
<para>Isto permite que muitos dos detalhes de implementação, como o formato das sequências de dados multimédia, a transparência de rede e as dependências de linguagens de programação, sejam escondidos da especificação da interface. Uma ferramenta, o &mcopidl;, traduz a definição da interface para uma linguagem de programação específica (de momento, só o C++ é que é suportado). </para>
<para>A ferramenta gera uma classe-esqueleto com todo o código de 'cola' e a funcionalidade de base. Você pode derivar a partir dessa classe para implementar as funcionalidades que deseja. </para>
<para>Na &IDL;, as interfaces são definidas de forma muito semelhante à de uma classe de C++ ou de uma estrutura do C, ainda que com algumas restrições. Como no C++, as interfaces podem ser subclasses de outras interfaces através de herança. As definições de interfaces podem incluir três coisas: sequências, atributos e métodos. </para>
<para>As sequências ou canais definem os dados multimédia, um dos componentes mais importantes de um módulo. As sequências são definidas no seguinte formato: </para>
<para>As sequências têm uma direcção definida na referência do módulo, tal como é indicado pelos qualificadores necessários 'in' ou 'out'. O argumento 'tipo' define o tipo dos dados, o qual poderá ser qualquer um dos tipos descritos posteriormente para os atributos (nem todos são suportados de momento). Muitos dos módulos usam o tipo de sequência 'audio', o qual é um nome alternativo para o 'float' (vírgula-flutuante) dado que é o formato interno dos dados usados para o áudio. Podem ser definidas várias sequências do mesmo tipo na mesma definição se usar nomes separados por vírgulas. </para>
<para>As sequências são síncronas por omissão, o que significa que elas são fluxos contínuos de dados a uma taxa constante, como o áudio <acronym>PCM</acronym>. O qualificador 'async' indica que é uma sequência assíncrona, a qual é usada para os fluxos de dados descontínuos. O exemplo mais comum de uma sequência assíncrona são as mensagens do &MIDI;. </para>
<para>A palavra-chave 'multi', que só é válida para as sequências de entrada, indica que a interface suporta um número variável de entradas. Isto é útil para implementar os dispositivos, como as mesas de mistura, que possam aceitar qualquer número de sequências de entrada. </para>
<para>Os atributos são os dados associados com uma instância de uma interface. Estes são declarados como as variáveis-membro do C++, e podem usar qualquer um dos tipos primitivos 'boolean', 'byte', 'long', 'string', ou 'float'. Você também poderá usar os tipos 'struct' ou 'enum' definidos pelo utilizador, assim como as sequências de tamanho variável, usando a sintaxe sequence<tipo>. Os atributos podem ser marcados opcionalmente como 'readonly' (apenas para leitura). </para>
<para>Tal como no C++, os métodos podem ser definidos nas interfaces. Os parâmetros dos métodos estão restringidos aos mesmos tipos dos atributos. A palavra-chave 'oneway' indica um método que devolve valores imediatamente e é executado assincronamente. </para>
<para>Já estão definidas várias interfaces-padrão de módulos para você no &arts;, como o <interfacename>StereoEffect</interfacename> e o <interfacename>SimpleSoundServer</interfacename>. </para>
<para>Um exemplo simples de um módulo retirado do &arts; é o módulo de atraso constante, o qual se encontra no ficheiro <filename>tdemultimedia/arts/modules/artsmodules.idl</filename>. A definição da interface está indicada em baixo. </para>
<para>Este módulo herda de <interfacename>SynthModule</interfacename>. Essa interface, definida no <filename>artsflow.idl</filename>, define os métodos-padrão que são implementados em todos os módulos de síntese de música. </para>
<para>O efeito CDELAY atrasa um canal estéreo pelo tempo indicado em 'time', o qual é um parâmetro de vírgula flutuante. A definição da interface tem um atributo do tipo 'float' para guardar o valor do atraso. Ele define duas sequências de áudio de entra e duas sequências de saída (o que é típico nos efeitos estéreo). Não são necessários mais métodos para além dos que ele já herdou. </para>
<para>O primeiro caso é o mais simples: depois de receber 200 amostras da entrada, o módulo produz 200 amostras de resultado. Só produz resultados à saída quando obtiver algo à entrada. </para>
<para>O segundo caso produz números diferentes de amostras de resultado, quando são dadas 200 amostras de entrada. Isto depende de qual a conversão que é efectuada, mas o número é conhecido antecipadamente. </para>
<para>O terceiro caso é ainda pior. Para o resultado, você nem sequer consegue adivinhar quantos dados as 200 amostras de entrada irão gerar (provavelmente muito mais do que 200 amostras à saída, mas...). </para>
<para>No &arts;s-0.3.4, só as sequências do primeiro tipo eram tratadas, e a maioria das coisas funcionavam. Isto é provavelmente o que você necessita para criar os módulos que processem áudio. O problema com os outros tipos mais complexos de transmissão é que eles são difíceis de programar e que você não precisa das suas funcionalidades na maior parte das vezes. É por isso que o processamento pode ser feito com dois tipos diferentes de sequências: síncronas e assíncronas. </para>
<para>A função <function>calculateBlock()</function> será chamada quando existirem dados suficientes disponíveis, e o módulo possa confiar nos ponteiros que referenciam os dados. </para>
<para>Os módulos poderão produzir dados algumas das vezes, ou com uma taxa de amostragem variável, ou apenas se eles tiverem dados de entrada de algum descritor de ficheiro. Eles não estão limitados pela regra <quote>deve ser capaz de atender pedidos de qualquer tamanho</quote>. </para>
<para>Sequências de saída: existem funções explícitas para alocar pacotes, para enviá-los - e um mecanismo de pesquisa ('polling') opcional que lhe dirá quando deverá criar mais dados. </para>
<para>Sequências de entrada: você obtém uma chamada sempre que recebe um novo pacote - você terá de dizer quando estiver a processar todos os dados desse pacote, o que não deverá acontecer de uma vez (você poderá dizer isso mais tarde e, se toda a gente já processou o pacote, este será libertado/reutilizado). </para>
<para>Quando você declara as sequências, você poderá usar a palavra-chave <quote>async</quote> para indicar que quer criar uma sequência assíncrona. Por isso, por exemplo, assuma que quer converter uma sequência assíncrona de 'bytes' para uma sequência síncrona de amostras. A sua interface poderia ser algo semelhante a isto: </para>
<para>Como é que você envia os dados? O primeiro método é chamado de <quote>push delivery</quote> (tradução livre: 'entrega por empurrão'). Com as sequências assíncronas, você envia os dados como pacotes. Isto significa que você envia pacotes individuais com 'bytes', como no exemplo acima. O processo actual é: alocar um pacote, preenchê-lo, enviá-lo. </para>
<para>Isto é muito simples, mas se precisar de enviar os pacotes à mesma rapidez com que o destinatário os consegue processar, é necessária outra aproximação, a <quote>pull delivery</quote> ('entrega por puxão'). Você pede para enviar os pacotes tão rapidamente quanto o destinatário está pronto para processar. Você começa com uma determinada quantidade de pacotes que envia. À medida que o destinatário vai processando um pacote a seguir ao outro, você começa a preenchê-los de novo com dados novos, enviando-os mais uma vez. </para>
<para>Isto significa que você deseja enviar os pacotes pela 'saida'. Você irá querer começar a enviar 8 pacotes de cada vez e, à medida que o destinatário processar alguns deles, você poderá querer preenchê-los de novo. </para>
<para>Foi então discutido o envio dos dados. A recepção dos mesmos é muito mais simples. Suponha que você tem um filtro ParaMinusculas, que simplesmente converte todas as letras para minúsculas: </para>
<para>Como pode ver, para cada pacote que chega, você obtém uma chamada a uma função ( a <function>process_entrada</function> no caso em questão). Você precisa de chamar o método <methodname>processed()</methodname> de um pacote para indicar que acabou de o processar. </para>
<para>Aqui está uma sugestão de implementação: se o processamento levar mais tempo (&ie; se precisar de esperar pelo resultado da placa de som ou algo do género), não invoque o 'processed' logo, mas armazene o pacote de dados em si e invoque o 'processed' apenas quando você processar de facto esse pacote. Desta forma, os emissores têm uma hipótese de saber quanto tempo leva mesmo a efectuar o seu trabalho. </para>
<para>Dado que a sincronização não é tão boa com as sequências assíncronas, você deverá usar as sequências síncronas sempre que possível e as assíncronas, só quando for necessário. </para>
<para>Imagine que tem 2 objectos, como por exemplo um ProdutorAudio e um ConsumidorAudio. O ProdutorAudio tem uma sequência de saída e o ConsumidorAudio tem uma de entrada. De cada vez que os tentar ligar, você irá usar estas duas sequências. A primeira utilização da predefinição é permitir-lhe fazer a ligação sem indicar os portos nesse caso. </para>
<para>Agora imagine que os dois objectos poderão lidar com o estéreo, e cada um possa ter um porto <quote>left</quote> (esquerdo) e <quote>right</quote> (direito). Você gostaria à mesma de os ligar de forma tão simples como anteriormente. Mas como é que o sistema de ligações saberia que porto de saída deveria ligar a que porto de entrada? Não tem nenhuma forma de mapear correctamente as sequências. A utilização de portos predefinidos poderá então ser adoptada para indicar várias sequências, com uma dada ordem. Desta forma, quando você ligar um objecto com 2 portos predefinidos a outro objecto com 2 portos predefinidos de entrada, não terá de indicar os portos e, nesse caso, o mapeamento será feito correctamente. </para>
<para>Obviamente, isto não está limitado ao estéreo. Pode-se tornar qualquer número de sequências como predefinidas se necessário, e a função 'connect' irá verificar se o número de predefinições para os dois objectos correspondem (na direcção necessária), se você não indicar os portos a usar. </para>
<para>A sintaxe é a seguinte: na &IDL;, você poderá usar a palavra-chave 'default' na declaração da sequência ou numa única linha. Por exemplo: </para>
<para>Neste exemplo, o objecto irá esperar que os seus dois portos de entrada estejam ligados por omissão. A ordem é a indicada na linha 'default', por isso, um objecto do tipo: </para>
<para>Fará as ligações do <quote>cuic</quote> à <quote>entrada1</quote> e do <quote>bzzt</quote> à <quote>entrada2</quote> automaticamente. Repare que, dado que só há uma saída para o misturador, será tornada predefinida nesse caso (ver em baixo). A sintaxe usada no gerador de ruído é útil para declarar uma ordem diferente da declaração, ou seleccionando apenas alguns portos como predefinidos. As direcções dos portos nesta linha serão pesquisados pelo &mcopidl;, por isso não os indique. Você até poderá misturar os portos de entrada e de saída numa linha destas, onde só a ordem é que é relevante. </para>
<para>Se for indicada uma lista 'default' na &IDL;, então use-a. Os portos-pai podem ser colocados também nesta lista, quer fossem predefinidos no 'pai' ou não. </para>
<para>Caso contrário, herde as predefinições do 'pai'. A ordem é do tipo 'pai1 predefinicao1, pai1 predefinicao2..., pai2 predefinicao1... Se existir um ascendente comum em dois ramos-pai, é feita uma junção do tipo <quote>virtual public</quote> na primeira ocorrência da predefinição na lista. </para>
<para>As notificações de alteração de atributos são uma forma de saber quando é que um atributo foi alterado. Eles são ligeiramente compatíveis com os 'signals' e 'slots' do &Qt; e do Gtk. Por exemplo, se você tiver um elemento gráfico, como uma barra deslizante, que configura um número entre 0 e 100, você poderá ter normalmente um objecto que faça algo com esse número (por exemplo, poderá controlar o volume de um sinal de áudio). Por isso, você gostaria que, de cada vez que movesse a barra, o objecto que ajustasse o volume fosse notificado. Uma ligação entre um emissor e um receptor. </para>
<para>O &MCOP; lida com isso, sendo capaz de oferecer notificações de cada vez que os atributos são alterados. Tudo o que for declarado como <quote>atributo</quote> na &IDL;, pode emitir essas notificações de alterações, e devê-lo-á fazer, sempre que for modificado. Tudo o que for declarado como <quote>atributo</quote> poderá também receber essas notificações de alteração. Por isso, se por exemplo tiver duas interfaces de &IDL;, como as seguintes: </para>
<para>Você podê-las-á ligar com as notificações de alteração. Isto funciona usando a operação normal de 'connect' do sistema de fluxo. Neste caso, o código de C++ para ligar dois objectos deverá ser semelhante ao seguinte: </para>
<para>Como pode ver, cada atributo oferece duas sequências diferentes, uma para enviar as notificações de alteração, chamada de <function><replaceable>nome-atributo</replaceable>_changed</function>, e outra para receber as notificações de alteração, chamada <function>nome-atributo</function>. </para>
<para>É importante saber que as notificações de alteração e as sequências assíncronas são compatíveis. São também transparentes na rede. Por isso, você consegue ligar uma notificação de alteração de um atributo 'float' de um elemento &GUI; a uma sequência assíncrona de um módulo de síntese a correr noutro computador. Isto, obviamente, implica que as notificações de alteração <emphasis>não sejam síncronas</emphasis>, o que significa que, depois de ter enviado a notificação de alteração, poderá levar algum tempo até que esta seja de facto recebida. </para>
<para>Ao implementar objectos que tenham atributos, você precisa de enviar as notificações de alteração sempre que um dado atributo muda. O código para o fazer é semelhante ao seguinte: </para>
<para>É bastante recomendado que use código deste género para todos os objectos que implementar, de modo a que as notificações de alterações possam ser usadas pelas outras pessoas. Você deverá, contudo, evitar o envio das notificações com demasiada frequências, por isso, se estiver a fazer processamento de sinal, é provavelmente melhor que você mantenha um registo de quando enviou a sua última notificação, para que não envie uma com cada uma das amostras que processar. </para>
<para>Será especialmente útil usar as notificações de alteração em conjunto com os osciloscópios (as coisas que visualizam os dados de áudio, por exemplo), os elementos gráficos, elementos de controlo e na monitorização. O código que usa isto está em <filename class="directory">tdelibs/arts/tests</filename> e na implementação experimental do 'artsgui', que poderá encontrar em <filename class="directory">tdemultimedia/arts/gui</filename>. </para>
<para>O ficheiro <literal role="extension">.mcoprc</literal> (na pasta pessoal de cada utilizador) poderá ser usado para configurar o &MCOP; em alguns aspectos. De momento, é possível o seguinte: </para>
<para>O nome de uma interface a usar para a comunicação global. A comunicação global é usada para procurar os outros objectos e obter o 'cookie' secreto. Vários clientes/servidores de &MCOP; que deverão ser capazes de falar entre si precisam de ter um objecto 'GlobalComm' que seja capaz de partilhar informações entre eles. De momento, os valores possíveis são o <quote>Arts::TmpGlobalComm</quote> para comunicar através da pasta <filename class="directory">/tmp/mcop-<replaceable>utilizador</replaceable></filename> (que irá funcionar apenas no computador local) ou o <quote>Arts::X11GlobalComm</quote> para comunicar através das propriedades da janela de topo do servidor X11. </para>
<para>Indica onde procurar pela informação do mediador. Você poderá indicar aqui mais do que uma pasta, separando as pastas com vírgulas, como no exemplo </para>
<para>Indica a partir de que pastas é que as extensões (na forma de bibliotecas dinâmicas) são carregadas. Podem ser indicados vários valores, separados por vírgulas. </para>
<title>O &MCOP; para os Utilizadores de <acronym>CORBA</acronym></title>
<para>Se já usou o <acronym>CORBA</acronym> antes, irá concluir que o &MCOP; é muito parecido, de facto. De facto, o &arts;, antes da versão 0.4 usava o <acronym>CORBA</acronym>. </para>
<para>A ideia básica do <acronym>CORBA</acronym> é a mesma: você implementa os objectos (componentes). Se usar as funcionalidades do &MCOP;, os seus objectos não estão só disponíveis como classes normais do mesmo processo (através das técnicas normais do C++) - eles passam a estar disponíveis de forma transparente para os servidores remotos. Para isto funcionar, a primeira coisa que precisa de fazer é indicar a interface dos seus objectos num ficheiro &IDL; - tal como na &IDL; do <acronym>CORBA</acronym>. Existem apenas algumas diferenças. </para>
<title>Funcionalidades do <acronym>CORBA</acronym> que Faltam no &MCOP;</title>
<para>No &MCOP; não existem parâmetros <quote>in</quote> e <quote>out</quote> nas invocações dos métodos. Os parâmetros são sempre de entrada e o código devolvido é sempre de saída, o que significa que a interface: </para>
<para>Não existem tipos 'union' nem 'typedef's. Não se sabe se é uma falha de facto, algo que uma pessoa necessitasse desesperadamente para sobreviver. </para>
<title>Funcionalidades do <acronym>CORBA</acronym> que São Diferentes no &MCOP;</title>
<para>Você declara as sequências como <quote>sequence<replaceable>tipo</replaceable></quote> no &MCOP;. Não existe necessidade de um 'typedef'. Por exemplo, em vez de: </para>
<para>Você pode declarar as sequências que serão então avaliadas pela plataforma do &arts;. As sequências são declaradas de forma semelhante aos atributos. Por exemplo: </para>
<para>Isto diz que o seu objecto irá aceitar duas sequências síncronas de áudio chamadas 'sinal1' e 'sinal2'. Por síncronas significa que são sequências que distribuem 'x' amostras por segundo (ou por outro período), de modo a que o escalonador garanta que lhe fornecerá uma quantidade balanceada de dados de entrada (⪚ existem 200 amostras de 'sinal1' para 200 amostras de 'sinal2'). Você garante que, se o seu objectivo for chamado com essas 200 amostras de 'sinal1' e 'sinal2', será capaz de produzir exactamente 200 amostras de 'saida'. </para>
<para>As cadeias de caracteres usam a classe de C++ do <acronym>STL</acronym> <classname>string</classname>. Quando é armazenado nas sequências, elas são armazenadas <quote>tal-e-qual</quote>, o que significa que são consideradas como sendo um tipo primitivo. Deste modo, elas precisam de ser copiadas. </para>
<para>As estruturas são todas derivadas da classe do &MCOP; <classname>Type</classname>, e são geradas no compilador de &IDL; do &MCOP;. Quando forem armazenadas em sequências, elas não serão guardadas <quote>tal-e-qual</quote> , mas sim como referências porque, caso contrário, iriam ocorrer várias cópias. </para>
<para>Depois de passar as interfaces pelo compilador de &IDL;, você precisa de derivar da classe <classname>_skel</classname>. Por exemplo, assuma que definiu a sua interface da seguinte forma: </para>
<para>Você pode passar isso pelo compilador de &IDL;, invocando o comando <userinput><command>mcopidl</command> <parameter>ola.idl</parameter></userinput>, o que por sua vez irá gerar o <filename>ola.cc</filename> e o <filename>ola.h</filename>. Para o implementar, você precisa de definir uma classe de C++ que herde do esqueleto: </para>
<programlisting>// ficheiro de inclusão de C++ - inclua o ola.h algures
<para>Logo que faça isso, você terá um objecto que poderá comunicar usando o &MCOP;. Basta criar um (usando as funcionalidades normais do C++ para criar um objecto): </para>
<para>Dado que os servidores de &MCOP; irão atender os pedidos num porto de <acronym>TCP</acronym>, potencialmente toda a gente (se você estiver na Internet) poderá tentar ligar-se aos serviços do &MCOP;. Por isso, é importante autenticar os clientes. O &MCOP; usa o protocolo 'md5-auth'. </para>
<para>De cada vez que um cliente se liga, ele verifica se o cliente conhece esse 'cookie' secreto, sem o transferir de facto (nem sequer de forma a que alguém que esteja a escutar o tráfego de rede o possa descobrir). </para>
<para>Para dar a cada cliente esse 'cookie' secreto, o &MCOP; irá (normalmente) colocá-lo na pasta <filename class="directory">mcop</filename> (em <filename class="directory">/tmp/mcop-<envar>USER</envar>/secret-cookie</filename>). Claro, você podê-lo-á copiar para outros computadores. Contudo, se o fizer, use um mecanismo de transferência seguro, como o <command>scp</command> (do <application>ssh</application>). </para>
<para>A autenticação dos clientes usa os seguintes passos: </para>
<para>[SERVIDOR] verifica que, ao baralhar o R e o S dará o mesmo resultado que o 'cookie' M recebido do cliente. Em caso afirmativo, a autenticação foi bem-sucedida. </para>
<para>O algoritmo de dispersão do MD5 não permite descobrir o <quote>texto original</quote>, que é o 'cookie' secreto S e o 'cookie' aleatório R (o qual é conhecido, de qualquer forma), a partir do 'cookie' baralhado M. </para>
<para>Antes de a autenticação ser bem sucedida, o servidor não irá receber outras mensagens da ligação. Em vez disso, se o servidor por exemplo estiver à espera de uma mensagem <quote>ClientHello</quote> e obtiver uma mensagem de 'mcopInvocation', irá quebrar a ligação. </para>
<para>Se o cliente não enviar uma mensagem de &MCOP; válida de todo (sem o código especial do &MCOP; no cabeçalho da mensagem) na fase de autenticação, mas sim outra coisa qualquer, a ligação é quebrada. </para>
<para>Se o cliente tentar enviar uma mensagem mesmo muito grande (> 4096 bytes na fase de autenticação, o tamanho da mensagem é truncado para 0 bytes, o que fará com que não seja aceite para a autenticação). Isto é para evitar que os clientes não-autenticados enviem mensagens de ⪚ 100 megabytes, o que faria com que fosse recebida e com que o servidor estoirasse com falta de memória. </para>
<para>Tem semelhanças conceptuais ao <acronym>CORBA</acronym>, mas pretende extendê-lo de todas as formas necessárias para as operações multimédia em tempo-real. </para>
<para>Oferece um modelo de objectos multimédia, o qual poderá ser usado para: a comunicação entre os componentes num espaço de endereçamento (um processo) e entre componentes que existam em tarefas, processos ou mesmo máquinas diferentes. </para>
<para>Tudo junto, será desenhado para uma performance extremamente alta (de modo a que tudo seja optimizado para ser extremamente rápido), o que é adequado para as aplicações multimédia bastante comunicativas. Por exemplo, a transmissão de vídeos é uma das aplicações do &MCOP;, onde a maioria das implementações de <acronym>CORBA</acronym> 'cairiam de joelhos'. </para>
<para>Se você souber que vai receber uma cadeia de caracteres, não precisa de a ler até encontrar o 'byte' zero para descobrir o seu tamanho, contudo, </para>
<para>Se você souber que vai receber uma sequência de cadeias de caracteres, você precisa de saber o tamanho de cada uma delas para descobrir o fim da sequência, dado que as cadeias de caracteres têm tamanho variável. Mas se você usar as cadeias de caracteres para algo útil, você terá de o fazer à mesma, por isso não se perde nada. </para>
<entry>é codificado como 4 'bytes', em que o 'byte' mais significativo vem primeiro, de modo a que o número 10001025 (correspondente a 0x989a81) será codificado como:</entry>
<entry><para>é codificado como um <type>long</type>, contendo o tamanho da cadeia de caracteres seguinte, e pela sequência de caracteres propriamente dita, terminando com um 'byte' a zeros (que está incluído na contagem do tamanho).</para>
<entry><para>é codificado como um 'byte', contendo 0 se for <returnvalue>false</returnvalue> (falso) ou 1 se for <returnvalue>true</returnvalue> (verdadeiro), de modo a que o valor booleano <returnvalue>true</returnvalue> é codificado como:</para></entry>
<entry><para>é codificado na representação de 4 'bytes' do IEEE754 - a documentação detalhada de como o IEEE funciona estão aqui: <ulink url="http://twister.ou.edu/workshop.docs/common-tools/numerical_comp_guide/ncg_math.doc.html">http://twister.ou.edu/workshop.docs/common-tools/numerical_comp_guide/ncg_math.doc.html</ulink> e aqui: <ulink url="http://java.sun.com/docs/books/vmspec/2nd-edition/html/Overview.doc.html">http://java.sun.com/docs/books/vmspec/2nd-edition/html/Overview.doc.html</ulink>. Deste modo, o valor 2,15 seria codificado como:</para></entry>
<para>Se você precisar de fazer referência a um tipo, todos os tipos primitivos são referidos pelos nomes indicados acima. As estruturas e os enumerados têm nomes próprios (como Header). As sequências são referidas como um *<replaceable>tipo normal</replaceable>, de modo que uma sequência de 'longs' é um <quote>*long</quote> e uma sequência de estruturas Header é um <quote>*Header</quote>. </para>
<para>Depois de receber o cabeçalho, o tratamento do protocolo (ligação) poderá receber a mensagem por completo, sem olhar para o seu conteúdo. </para>
<para>O 'messageLength' no cabeçalho é, obviamente em alguns casos, redundante, o que significa que esta aproximação não é minimalista no que respeita ao número de 'bytes'. </para>
<para>Contudo, conduz a uma implementação simples (e rápida) do processamento não-bloqueante de mensagens. Com a ajuda do cabeçalho, as mensagens poderão ser recebidas pelas classes de tratamento do protocolo em segundo plano (não-bloqueante), se existirem demasiadas ligações ao servidor, todas elas poderão ser servidas em paralelo. Você não precisa de olhar para o conteúdo da mensagem para a receber (e para determinar que terminou), basta olhar para o cabeçalho, por isso o código para isso é relativamente simples. </para>
<para>Logo que esteja lá uma mensagem, esta poderá ser descodificada e processada num único passo, sem se preocupar com os casos em que nem todos os dados foram recebidos (porque o 'messageLength' garante que está lá tudo). </para>
<para>Para invocar um método remoto, você precisa de enviar a seguinte estrutura no corpo de uma mensagem de &MCOP; com o messageType = 1 (mcopInvocation): </para>
<para>depois disso, você envia os parâmetros como uma estrutura, ⪚ se você invocar o método 'concatenar(string s1, string s2)', você envia uma estrutura do tipo </para>
<para>se o método foi declarado como 'só de ida0 - o que significa que é assíncrono sem código devolvido - então é tudo. Caso contrário, você iria receber como resposta uma mensagem com o messageType = 2 (mcopReturn) </para>
<para>em que o <tipo-a-devolver> é o tipo do resultado. Como os tipos 'void' são omitidos na codificação, você também poderá só escrever o 'requestID' se regressar de um método 'void'. </para>
<para>Para fazer as invocações, você teria de conhecer os métodos que um objecto suporta. Para o fazer, o 'methodID' 0, 1, 2 e 3 estão pré-destinados a certas funcionalidades. Isto é </para>
<para>o campo 'parameters' contém os componentes do tipo que indicam os tipos dos parâmetros. O tipo do valor devolvido é indicado no campo 'type' do MethodDef. </para>
<para>De forma restrita, só os métodos <methodname>_lookupMethod()</methodname> e <methodname>_interfaceName()</methodname> são diferentes de objecto para objecto, enquanto que o <methodname>_queryInterface()</methodname> e o <methodname>_queryType()</methodname> são sempre os mesmos. </para>
<para>O que são esses methodIDs? Se você fizer uma invocação de &MCOP;, irá ficar à espera de passar um número para o método que você está a invocar. A razão para tal é que os números podem ser processados muito mais depressa do que as cadeias de caracteres ao executar um pedido de &MCOP;. </para>
<para>Por isso, como é que obtém esses números? Se você souber a assinatura do método, existe um MethodDef que descreve o método (e que contém o nome, o tipo, os nomes e os tipos de parâmetros, entre outras coisas) e você poderá passar isso ao '_lookupMethod' do objecto onde deseja invocar um método. Dado que o '_lookupMethod' está pré-associado ao 'methodID' 0, você não deverá ter problemas ao fazê-lo. </para>
<para>Por outro lado, se você não souber a assinatura do método, você poderá descobrir os métodos que são suportados, usando o '_interfaceName', o '_queryInterface' e o '_queryType'. </para>
<title>Porque é que o &arts; Não Usa o &DCOP;</title>
<para>Dado que o &kde; largou o <acronym>CORBA</acronym> por completo e está a usar o &DCOP; em todo o lado, naturalmente a questão levantar-se-á: 'porque é que o &arts; não o faz também?'. No fim de tudo, o suporte do &DCOP; está no <classname>TDEApplication</classname>, é bem-mantido, supostamente integra-se bem com a libICE, entre outras coisas. </para>
<para>Dado que existirá (potencialmente) uma grande quantidade de pessoas a perguntar se ter o &MCOP; para além do &DCOP; é realmente necessário, aqui está a resposta. Não levem o autor a mal, porque não está a dizer <quote>o &DCOP; é mau</quote>. Simplesmente quer dizer <quote>o &DCOP; não é a solução adequada para o &arts;</quote> (embora seja uma boa solução para outras coisas). </para>
<para>Primeiro, você precisa de compreender para que é que o &DCOP; foi criado. Desenvolvido em dois dias durante o encontro &kde;-TWO, pretendia ser o mais simples possível, um protocolo de comunicações realmente <quote>leve</quote>. Especialmente, a implementação descartou tudo o que pudesse envolver complexidade, como por exemplo um conceito completo de como os tipos de dados seriam codificados. </para>
<para>Ainda que o &DCOP; não se preocupe com certas coisas (por exemplo: como é que se envia uma cadeia de caracteres de forma transparente na rede?) - isso precisa de ser feito. Por isso, tudo o que o &DCOP; não faz, fica a cargo do &Qt; nas aplicações do &kde; que usam o &DCOP; hoje em dia. Isto é em grande parte gestão de tipos (usando o operador de serialização do &Qt;). </para>
<para>Por isso, o &DCOP; é um protocolo mínimo que permite perfeitamente às aplicações do &kde; enviarem mensagens simples do tipo <quote>abrir uma janela a apontar para http://www.kde.org</quote> ou <quote>os seus dados de configuração mudaram</quote>. Contudo, dentro do &arts; o foco situa-se noutras coisas. </para>
<para>A ideia é que alguns pequenos 'plugins' do &arts; irão comunicar, trocando algumas estruturas de dados como <quote>eventos MIDI</quote> e <quote>ponteiros de posição na música</quote> ou <quote>grafos de fluxo</quote>. </para>
<para>Estes são tipos de dados complexos, os quais deverão ser enviados entre objectos diferentes e passados como sequências ou parâmetros. O &MCOP; fornece um conceito de tipos para definir dados complexos a partir de dados mais simples (de forma semelhante às estruturas ou vectores no C++). O &DCOP; não se preocupa com os tipos de todo, por isso este problema seria deixado para o programador - como: criar classes de C++ para os tipos, certificando-se que eles pudessem serializar-se correctamente (por exemplo: suportar o operador de transmissão do &Qt;). </para>
<para>Mas desta forma, eles seriam inacessíveis a tudo o que não fosse codificação directa de C++. Especificamente, você não poderia desenhar uma linguagem de 'scripting' que conhecesse todos os 'plugins' de tipo que poderão estar expostos, dado que eles não se descrevem a si próprios. </para>
<para>O mesmo argumento se aplica também às interfaces. Os objectos do &DCOP; não expõem as suas relações, hierarquias de herança, etc. - se você fosse criar um navegador de objectos que lhe mostrasse <quote>que atributos tem tido este objecto</quote>, seria mal-sucedido. </para>
<para>Embora o Matthias tenha dito que existe uma função especial <quote>functions</quote> em cada objecto que lhe indica os métodos que um dado objecto suporta, isto deixa de fora coisas como os atributos (propriedades), as sequências e as relações de herança. </para>
<para>Isto quebra seriamente as aplicações como o &arts-builder;. Mas lembre-se. o &DCOP; não pretendia ser um modelo de objectos (dado que o &Qt; já tem um com o <application>moc</application> e semelhantes), nem pretende ser algo semelhante ao <acronym>CORBA</acronym>, mas simplesmente uma forma de fornecer comunicação entre aplicações. </para>
<para>Porque ainda o &MCOP; existe é: deverá funcionar perfeitamente com canais entre objectos. O &arts; faz um uso intensivo de pequenos 'plugins', que se interligam entre si com as sequências. A versão em <acronym>CORBA</acronym> do &arts; tinha de introduzir uma divisão muito incómoda entre <quote>os objectos SynthModule</quote>, os quais eram os módulos de trabalho internos que faziam a transmissão e <quote>a interface <acronym>CORBA</acronym></quote>, que era algo externo. </para>
<para>Muito do código preocupava-se em fazer a interacção entre <quote>os objectos SynthModule</quote> e <quote>a interface <acronym>CORBA</acronym></quote> parecer natural, mas não o era, porque o <acronym>CORBA</acronym> não sabia nada de nada sobre as sequências. O &MCOP; sabe. Olhe para o código (algo como o <filename>simplesoundserver_impl.cc</filename>). Muito melhor! As sequências podem ser declaradas na interface dos módulos e implementadas de forma natural. </para>
<para>Ninguém o pode negar. Uma das razões pela qual o &MCOP; foi feito foi a velocidade. Aqui estão alguns argumentos porque é que o &MCOP; será definitivamente mais rápido que o &DCOP; (sem sequer mostrar imagens). </para>
<para>Uma invocação do &MCOP; terá um cabeçalho com seis <quote>long</quote>s. Isto é: </para>
<para>Depois disso, seguem-se os parâmetros. Repare que a descodificação destes é extremamente rápida. Você poderá usar pesquisas em tabelas para encontrar o objecto e a função de descodificação do método, o que significa que a complexidade é O(1) (levará a mesma quantidade de tempo, independentemente de quantos objectos estão vivos, ou de quantas funções existem). </para>
<para>No &DCOP;, todos os pedidos são passados através de um servidor (o <application>DCOPServer</application>). Isto significa que o processo de uma invocação síncrona se assemelha a: </para>
<para>O <application>DCOPserver</application> (o homem-no-meio) recebe a invocação e procurar onde precisa de ir, enviando para o servidor <quote>real</quote>. </para>
<para>Digamos que ambos foram implementados correctamente, a estratégia ponto-a-ponto do &MCOP; deverá ser mais rápida por um factor de 2, do que a estratégia do 'homem-no-meio' do &DCOP;. Repare contudo que existiam razões para escolher a estratégia do &DCOP;, nomeadamente: se você tiver 20 aplicações a correr, se cada uma estiver a falar com outra, você precisa de 20 ligações no &DCOP;, e de 200 com o &MCOP;. Contudo, no caso do multimédia, isto não suposto ser a configuração normal. </para>
<para>Tentou-se comparar o &MCOP; com o &DCOP;, fazendo uma invocação do tipo 'adicionar dois números'. Alterou-se o 'testdcop' para conseguir isto. Porém, o teste pode não ter sido fidedigno do lado do &DCOP;. Invocou-se o método no mesmo processo que fez a chamada para o &DCOP;, e não se soube como se ver livre de uma mensagem de depuração, pelo que foi feita redireccionamento do resultado. </para>
<para>O teste só usava um objecto e uma função, esperando que os resultados do &DCOP; começassem a descer com mais objectos e funções, enquanto os resultados do &MCOP; se deveriam manter iguais. Também, o processo do <application>dcopserver</application> não estava ligado a nenhumas outras aplicações, pelo que, se estivessem outras aplicações ligadas, a performance do encaminhamento iria decrescer. </para>
<para>O resultado obtido foi que, enquanto o &DCOP; obteve cerca de 2000 invocações por segundo, o &MCOP; obteve um pouco mais de 8000 invocações por segundo. Isto resulta num factor de 4. Sabe-se que o &MCOP; não está ajustado para o máximo possível, ainda. (Comparação: o <acronym>CORBA</acronym>, implementado no 'mico', faz algo entre 1000 e 1500 invocações por segundo). </para>
<para>Se você quiser dados <quote>mais em bruto</quote>, pense em fazer alguma aplicação de medida de performance para o &DCOP; e envie-a para o autor. </para>
<para>O <acronym>CORBA</acronym> tinha a funcionalidade interessante de que você poderia usar os objectos que você implementou uma vez, como <quote>processos de servidor separados</quote> ou como <quote>bibliotecas</quote>. Você poderia usar o mesmo código para tal, e o <acronym>CORBA</acronym> iria decidir de forma transparente o que fazer. Com o &DCOP;, não é realmente pretendido, e tanto quanto se sabe não é possível realmente. </para>
<para>O &MCOP;, por outro lado, deverá suportá-lo logo desde o início. Por isso, você poderá correr um efeito dentro do &artsd;. Mas se você for um editor de ondas, você poderá optar por correr o mesmo efeito dentro do espaço do processo. </para>
<para>Embora o &DCOP; sej, em grande medida, uma forma de comunicar entre as aplicações, o &MCOP; também o é. Especialmente para a transmissão multimédia, o que é importante (dado que você poderá correr vários objectos &MCOP; em paralelo, para resolver uma tarefa de multimédia na sua aplicação). </para>
<para>Ainda que o o &MCOP; não o faça de momento, as possibilidades estão em aberto para implementar as funcionalidades de qualidade de serviço. Algo do género <quote>aquele evento &MIDI; é mesmo MUITO importante, em comparação com esta invocação</quote>. Ou algo do tipo <quote>necessita de estar ali a tempo</quote>. </para>
<para>Por outro lado, a transferência de sequências poderá ser integrada no protocolo &MCOP; sem problemas e ser combinada com elementos de <acronym>QoS</acronym>. Dado que o protocolo poderá ser alterado, a transferência de sequências do &MCOP; não deverá ser mais lenta do que uma transmissão convencional de <acronym>TCP</acronym>, mas: é mais fácil e mais consistente de usar. </para>
<para>Não existe necessidade de basear uma plataforma de multimédia no &Qt;. Ao decidir isso, e usando toda aquela serialização e outras funcionalidades giras do &Qt;, iria conduzir facilmente a que a plataforma se tornasse apenas para o &Qt; ou (para apenas para o &kde;). Quer dizer: assim que se vir que os GNOMEs comecem a usar o &DCOP;, também, ou algo do género, provavelmente o autor ficará errado. </para>
<para>Enquanto se sabe que o &DCOP; basicamente não sabe nada sobre os tipos de dados que envia, de modo que você poderia usar o &DCOP; sem usar o &Qt;, veja como é que é usado na utilização do dia-a-dia do &kde;: as pessoas enviam tipos como o <classname>TQString</classname>, o <classname>QRect</classname>, o <classname>QPixmap</classname>, o <classname>QCString</classname>, ..., de um lado para o outro. Estes usam a serialização do &Qt;. Por isso, se alguém optar por suportar o &DCOP; num programa do GNOME, ele teria de afirmar que usava os tipos <classname>TQString</classname>,... (ainda que não o faça, de facto) e emular a forma como o &Qt; faz a transmissão, ou então teria de enviar outros tipos de cadeias de caracteres, imagens e rectângulos, o que deixaria de ter possibilidades de interoperabilidade. </para>
<para>Bem, seja o que for, o &arts; pretendeu sempre funcionar com ou sem o &kde;, com ou sem o &Qt;, com ou sem o X11, e talvez com ou sem o &Linux; (e não há problema nenhum com as pessoas que o transpõem para um sistema operativo proprietário conhecido). </para>
<para>É a posição do autor que os componentes não&GUI; deverão ser criados de forma independente da &GUI;, para possibilitar a partilha destes pelas quantidades maiores de programadores (e utilizadores) possível. </para>
<para>É óbvio que o uso de dois protocolos de <acronym>IPC</acronym> pode causar problemas. Ainda mais, se ambos não forem normas. Contudo, pelas razões indicadas acima, a mudança para o &DCOP; não é uma opção. Se existir um interesse significativo em encontrar uma forma de unir os dois, ok, poderemos tentar. Até poderemos tentar fazer com que o &MCOP; 'fale' <acronym>IIOP</acronym>, onde então poderemos passar a ter um <acronym>ORB</acronym> de <acronym>CORBA</acronym>. </para>
<para>Falou-se com o Matthias Ettrich um pouco sobre o futuro dos dois protocolos e encontraram-se montes de formas como as coisas poderão seguir daqui para a frente. Por exemplo, o &MCOP; poderia tratar da comunicação de mensagens no &DCOP;, colocando os protocolos um pouco mais juntos entre si. </para>
<para>Por isso, algumas soluções possíveis seriam: </para>
<para>Criar uma 'gateway' de &MCOP; - &DCOP; (o qual deverá ser possível, e possibilitaria a interoperabilidade) - nota: existe um protótipo experimentar, se quiser ver algo sobre o assunto. </para>
<para>Integrar tudo o que os utilizadores do &DCOP; esperam no &MCOP;, e tentar apenas usar o &MCOP; - uma pessoa até poderia adicionar uma opção de <quote>homem-no-meio</quote> no &MCOP;, também ;) </para>
<para>Contudo, poderá não ser a pior possibilidade para usar cada protocolo em tudo em que se pensou usar (existem algumas grandes diferenças nos objectivos de desenho), e não vale a pena tentar juntá-los num só. </para>