BlackTDN Search

sábado, 11 de setembro de 2010

Protheus :: Diretivas do Pré-Processador, Constantes Simbólicas e outros Bichos

Pré-Processador:

O protheus, bem como todos os compiladores derivados do CA-Clipper, possuem um recurso chamado de "Pré-Processador" (Outros compiladores também possuem como C, C++, Assembler, etc).

É o "pré-processador" quem prepara o código fonte para a compilação. Ele tem a finalidade de efetuar busca e substituição das "diretivas de pré-processamento" de forma a evitar erros durante a compilação e ou execução do código.

Podemos elencar os seguintes recursos de "pré-processamento":

  • - Constantes Simbólicas
  • - Arquivos include
  • - Macros do compilador
  • - Compilação condicional
  • - Comandos Definidos pelo Usuário

Constantes Simbólicas:

As constantes simbólicas são definidas com a diretiva do "pré-processador" #DEFINE. O "pré-processador" procura todas as constantes simbólicas definidas por essa diretiva substituindo-as pelo seu real valor.
Por exemplo:


#DEFINE FO_READ         0 //Open for reading (default)
#DEFINE FO_WRITE        1 //Open for writing
#DEFINE FO_READWRITE    2 //Open for reading or writing
#DEFINE FO_SHARED      64 //Allow others to read or write

nHandle := FOPEN("Temp.txt", FO_READWRITE + FO_SHARED)
IF FERROR() != 0
  ConOut( "Cannot open file, DOS error " , FERROR() )
ENDIF


O "pré-processador" irá traduzir para


[linha em branco]
[linha em branco]
[linha em branco]
[linha em branco]

nHandle := FOPEN("Temp.txt", 2 + 64 )
IF FERROR() != 0
  ConOut( "Cannot open file, DOS error " , FERROR() )
ENDIF


O "pré-processador", no arquivo de saída, remove a diretiva #DEFINE deixando linhas em branco para preservar a numeração das linhas e substitui os nomes das constantes pelos seus valores.

A diretiva #DEFINE, para o pré-processador, é sensivel ao contexto. Então devemos usá-las da forma que foram declaradas, se tentarmos usa-la como:

nHandle := FOPEN("Temp.txt", fo_readwrite + fo_shared)

O "pré-processador não irá subsituir as constantes pelos seus reais valores e obteremos um erro em tempo de execução. O sistema tentará interpretar fo_readwrite e fo_shared como variáveis e gerará uma exceção.

Então, vale salientar, que a tradução de #DEFINE feita pelo "pré-processador" é sensivel a letras maiúsculas e minúsculas.

Arquivos Include:

A grande maioria das diretivas de "pré-processamento" do protheus são definidas em arquivos de cabeçalho, que normalmente possuem a extensão .CH (Clipper Header) Para fazer uso delas faz-se necessário usar a diretiva #INCLUDE. Então, para que o pré-processador possa buscar e substituir o valor real para a constante FO_READWRITE devemos incluir o arquivo de cabeçalho "FILEIO.CH" no inicio do programa como:

#INCLUDE "FILEIO.CH"

e, durante o "pré-processamento" todas as referencias a FO_READWRITE serão substituidas pelo seu valor real que no arquivo "FILEIO.CH" está definido como 2. Os arquivos de cabeçalho são muito usados para padronizar os valores das constantes simbólicas, macros do compilador e comandos definidos pelo usuário.

Macros do Compilador:

As "macros do compilador" poderão ser usadas para simular o uso de "funções in line" no protheus. Use-as quando necessitar repetir uma pequena sequencia de codificação várias vezes mas que tornaria o processo custoso se uma função real fosse utilizada. Por Exemplo: Para que criar a função Max( nVal1 , nVal2 ) ou Min( nVal1 , nVal2 ) se podemos criar uma macro para esse fim:

Ex.:


#DEFINE Max( a , b ) IIF( a > b , a , b )
#DEFINE Min( a , b ) IIF( a < b , a , b )
E usa-las como:

nMax := Max( 1 , 2 )
nMin := Min( 1 , 2 )
que o "pré-processador" substituirá por:

nMax := IIF( 1 > 2 , 1 , 2 )
nMin := IIF( 1 < 2 , 1 , 2 )
A vantagem de usar as macros do compilador é que elas não precisam ser empilhadas, não precisa passar pelo complexo processo que as funções passam. A desvantagem de usar as macros do compilador para simular "funções inline" no protheus, é que a diretiva #DEFINE é sensível a maiúsculas e minúsculas. Então, para evitarmos erro durante a execução, teriamos que prever todas as possibilidades para a macro como em:

#DEFINE Max( a , b ) IIF( a > b , a , b )
#DEFINE MAx( a , b ) IIF( a > b , a , b )
#DEFINE MAX( a , b ) IIF( a > b , a , b )
#DEFINE mAX( a , b ) IIF( a > b , a , b )
#DEFINE maX( a , b ) IIF( a > b , a , b )
#DEFINE max( a , b ) IIF( a > b , a , b )
#DEFINE MaX( a , b ) IIF( a > b , a , b )
#DEFINE mAx( a , b ) IIF( a > b , a , b )
...

#DEFINE Min( a , b ) IIF( a > b , a , b )
#DEFINE MIn( a , b ) IIF( a > b , a , b )
#DEFINE MIN( a , b ) IIF( a > b , a , b )
#DEFINE mIN( a , b ) IIF( a > b , a , b )
#DEFINE miN( a , b ) IIF( a > b , a , b )
#DEFINE min( a , b ) IIF( a > b , a , b )
#DEFINE MiN( a , b ) IIF( a > b , a , b )
#DEFINE mIn( a , b ) IIF( a > b , a , b )
...
Isso seria muito trabalhoso, então, podemos abrir mão de outra diretiva do pré-processador para simular funções "in line", a diretiva #TRANSLATE ou #XTRANSLATE como:

#TRANSLATE Max( <a> , <b> ) => IIF( <a> \> <b> , <a> , <b> )
#XTRANSLATE Min( <a> , <b> ) => IIF( <a> \< <b> , <a> , <b> )
E poderemos chama-la de qualquer forma, ex:

nMax := Max( 1 , 2 )
nMax := MAx( 4 , 3 )
nMax := MAX( 10 , 20 )
nMax := mAX( 45 , 3 )
ou

nMin := Min( 1 , 2 )
nMin := MIn( 15 , 12 )
nMin := MIN( 1970 , 10 )
nMin := mIN( 3 , 1 )
que serão traduzidas corretamente pelo pré-processador para

nMax := IIF( 1 > 2 , 1 , 2 )
nMax := IIF( 4 > 3 , 4 , 3 )
nMax := IIF( 10 > 20 , 10 , 20 )
nMax := IIF( 45 > 3 , 45 , 3 )

ou 

nMin := IIF( 1 < 2  , 1 , 2)
nMin := IIF( 15 < 12 , 15 , 12 )
nMin := IIF( 1970 < 10 , 1970 , 10 )
nMin := IIF( 3 < 1 , 3 , 1 )


Compilação condicional:

O pré-processador trata a compilação condicional incluindo ou excluindo partes do código para a real compilação de acordo com uma condição. As diretivas para a compilação condicional são:

#IFDEF - Verifica se um símbolo está definido
#IFNDEF - Verifica se um símbolo não está definido
#ELSE - Incluir algo se um #IFDEF ou #IFNDEF "falhar"
#ENDIF - Indica o final da codificação controlada pelas diretivas #IFDEF, #IFNDEF e #ELSE.
Podemos definir um símbolo sem abrituir um valor a ele. Podemos escrever #DEFINE DEBUG e testá-lo em seguida:

#IFDEF DEBUG
 ConOut( "Estou em processo de depuração" )
#ENDIF
Podemos definir simbolos, também, à partir da IDE do Protheus, nos parâmtros para compilação passando-os da seguinte forma D/DEBUG cria o símbolo DEBUG que poderá ser verficado da mesma forma (um exemplo poderá ser obtido em: "Protheus IDE:: Compilação Condicional", ou ainda em "Sudoku :: Tutorial"). Existem vários simbolos condicionais definidos no protheus, o principal e mais usado é o TOP, para verificar se o RPO a ser utilizado é TOPCONNECT ou não.

#IFDEF TOP
 cQuery := "SELECT * FROM " + RetSqlName( "SA1" )
#ENDIF

Essa parte do código só será incluida na versão definitiva do código a ser compilado se o símbolo TOP estiver definido.

Vou deixar os comandos definidos pelo usuário para os próximos "posts".

[]s
иαldσ dj

Baseado no original de Rick Spence (Spence, Rick - Clipper 5.2 / Rick Spence - Makron Books, 1994)

Um comentário:

  1. Desafio.

    Decriptar os novos INCLUDES do P10.

    Já tentei várias funções,
    Abrir o arquivo com FOPEN()
    Usar;
    Encript()
    TarDecomp()
    MSDecomp()
    GZDecomp()

    Ao abrir o arquivo com Notepad++
    aparece a string #zip como parte de um cabeçalho.

    Por hoje eu parei minhas tentativas,
    pq sinto a necessidade de amar a minha mulher!

    Fica ai entao um desafio.
    vc que é um cara esperto, que veio do ninho microsiga,rs!

    Ah, tenho visto seus fontes antigos da epoca de microsiga, vc deve conhecer o pessoal da antiga lá, como o riera o pai do PCO.

    Abs!


    MS

    ResponderExcluir