Sintaxe

 

#define <constante> [ <texto de substituição> ]

#define <função> ( [ <lista de argumentos> ] ) [ < expressão > ]

 

Propósito

 

A diretiva #define define uma constante ou uma pseudofunção para o compilador.

 

Argumentos

 

<constante>

 

Define o nome da constante.

 

<texto de substituição>

 

Opcionalmente, pode-se definir um texto para substituir uma constante válida sempre que a mesma for encontrada pelo compilador.

 

<função>

 

Define o nome de uma pseudofunção com uma lista opcional de argumentos. A função deve sempre ser seguida por um abre parênteses, sem nenhum espaço em branco entre si. A lista de argumentos, caso exista, deve sempre ser seguida por um fecha parênteses, sem nenhum espaço em branco entre si. Caso a lista de argumentos não exista, a função deve ser seguida por um abre e fecha parênteses, sem espaços em branco.

 

<lista de argumentos>

 

Define a lista de argumentos da função, separando-os por vírgulas.

 

<expressão>

 

Define a expressão que substituirá a pseudofunção definida, sempre que ela for encontrada pelo compilador. Esta expressão deve estar entre parênteses para garantir a precedência de sua avaliação quando a pseudofunção for expandida.

 

Utilização

 

Essa diretiva define um nome identificador e, opcionalmente, associa-lhe um texto de substituição. Se o texto de substituição for especificado, o compilador substituirá a constante pelo texto sempre que a encontrar numa linha do código-fonte do arquivo de programa (.PRW) no qual ela foi definida.

 

Os nomes identificadores especificados através da diretiva #define seguem quase todas as regras de nomenclatura de variáveis do AdvPL. A principal exceção é que os identificadores definidos pela diretiva #define são case-sensitives, ou seja, diferenciam letras maiúsculas de minúsculas. Portanto, o identificador
ident1 é diferente do identificador IDENT1, por exemplo.

 

Por convenção, os identificadores definidos pela diretiva #define são sempre especificados em letras maiúsculas, para serem facilmente distinguidos dos outros identificadores de um programa. Além disso, convenciona-se também que os identificadores #define são iniciados por uma ou duas letras padronizadas como prefixo, com o objetivo de agrupar determinados tipos de identificadores, além de garantir a sua unicidade.

 

O arquivo cabeçalho PROTHEUS.CH, contido no diretório INCLUDE abaixo do diretório raiz do Protheus, é um bom exemplo da utilização da diretiva #define para definir identificadores que facilitam o uso de diversas funções padrão do AdvPL.

 

Cada definição feita pela diretiva #define deve ocorrer isoladamente em uma linha de instrução. Embora uma única definição possa ser continuada por diversas linhas físicas, através do ponto-e-vírgula, o AdvPL não permite que uma única linha lógica de instrução contenha mais de uma definição #define. Cada diretiva #define deve ser colocada numa linha de instrução própria, sendo seguida por um ou mais espaços em branco, ou tabulações, por um único nome identificador e opcionalmente pelo texto de substituição. Entretanto, definições podem ser intercaladas. Isto é, um identificador pode definir outro.

 

Um identificador definido através da diretiva #define é válido apenas para as funções contidas no arquivo de programa (.PRW) no qual foi definido. Uma vez definido, o identificador torna-se visível a partir da linha de instrução que o definiu até a última linha do arquivo de programa (.PRW) ou até que seja cancelado, por meio da diretiva #undef, ou redefinido.

 

Identificadores já definidos pela diretiva #define poderão ser cancelados ou redefinidos. Para redefini-los, basta executar uma nova diretiva #define com o mesmo nome identificador, atribuindo-lhe um novo texto de substituição. Neste caso, uma mensagem de advertência do compilador será apresentada. Porém, ela não constituirá um erro, mas apenas um aviso para chamar a atenção sobre a redefinição efetuada.

 

Para cancelar um identificador deve se utilizar a diretiva #undef, tendo como seu argumento o nome do identificador a ser cancelado.

 

A diretiva #define possui três propósitos básicos:

 

  1. Definir um identificador de controle para as diretivas #ifdef e #ifndef;
  2. Definir uma constante, ou seja, um identificador para representar um valor constante dentro do programa;
  3. Definir uma pseudofunção para o compilador.

 

Identificadores para o compilador

 

Este tipo de identificador é definido através da diretiva #define sem a especificação do texto de substituição. É normalmente utilizado no teste de existência de um identificador efetuado pelas diretivas #ifdef e #ifndef.

Estas diretivas permitem a determinação de compilações condicionais, baseadas na existência ou não de um identificador, causando a inclusão ou a exclusão de um determinado trecho do código-fonte no processo de compilação.

 

Constantes

 

Neste caso, a diretiva #define é utilizada para atribuir um nome identificador para um determinado valor constante que será utilizado pelo programa. Sempre que este identificador for encontrado pelo compilador numa linha do programa, será substituído pelo valor constante especificado. A utilização típica deste tipo de identificador é exemplificada abaixo:

 

#define K_ESC     27

     ...

     <instruções>

     ...

 

IF LASTKEY() == K_ESC

          RETURN

ENDIF

 

Neste exemplo, o identificador K_ESC representa o valor constante 27, que corresponde ao código da função INKEY() para a tecla <Esc>. Portanto, quando o compilador encontrar o identificador K_ESC, ele o substituirá pelo valor constante 27, que será efetivamente processado durante a execução do programa.

 

Embora este tipo de problema possa ser resolvido através da utilização de variáveis de memória normais para armazenar os valores constantes, há uma série de vantagens na utilização de identificadores definidos através da diretiva #define para a representação de valores constantes. Entre estas vantagens podemos citar:

 

  • O compilador gera um código mais compacto e mais rápido;
  • O programa utilizará menos memória durante a sua execução;
  • O programa será executado com mais rapidez;
  • Não ocorrerá mais um erro conceitual normalmente cometido quando se usa uma variável para armazenar um valor constante;
  • O AdvPL fornece uma série de arquivos cabeçalho (header files) que já definem um conjunto de identificadores para representar os valores constantes utilizados por diversas funções. A inclusão destes arquivos cabeçalho, através da diretiva #include, facilita muito a utilização das funções em termos de programação;
  • A clareza e a legibilidade dos programas é sensivelmente melhorada com o uso de identificadores que seguem as convenções dos arquivos cabeçalho definidos pelo AdvPL. Por exemplo, ao invés de decorar todos os códigos das teclas, é melhor ler o nome dos identificadores: K_ESC ao invés de 27, K_RETURN ao invés de 13, e assim por diante;
  • A manutenção dos programas torna-se mais rápida, pois a definição dos identificadores de constantes fica localizada num mesmo ponto;
  • As definições dos identificadores de constantes e de outros identificadores podem ser agrupadas em arquivos específicos: os arquivos cabeçalho. Estes podem ser “incluídos” num determinado programa, através da diretiva #include, permitindo que as mesmas definições sejam utilizadas por diversos programas, grupos de programas ou aplicações inteiras. Esta técnica gera uma maior padronização, organização e otimização do seu código-fonte;
  • A utilização de identificadores para constantes torna o programa mais independente do seu ambiente operacional e até do equipamento utilizado.

 

Pseudofunções

 

A diretiva #define também pode definir pseudofunções durante a fase de compilação. Uma pseudofunção corresponde basicamente a um identificador que é substituído por uma expressão. Por exemplo:

 

#define RCUB(nX)                      (nX ^ 1 / 3)                    // Raiz cúbica

#define ARET(nLarg, nComp)     (nLarg * nComp)          // Área de um retângulo

#define ATRI(nBase, nAltura)     (nBase * nAltura / 2)     // Área de um triângulo

 

Durante a fase de compilação, sempre que o compilador encontra uma chamada a uma função que coincide com a definição de uma pseudofunção, ele substitui a pseudofunção pela expressão correspondente. Por exemplo:

 

MSGALERT( RCUB(9) )          // É substituída por (9 ^1 / 3)

MSGALERT( ARET(5, 10) )     // É substituída por (5 * 10)

MSGALERT( ATRI(10, 5) )      // É substituída por (10 * 5 / 2)

 

Ao se utilizar a diretiva #define na definição de pseudofunções e ao se utilizar as pseudofunções definidas, algumas regras devem ser observadas:

 

  1. A expressão que corresponde à função e fornecerá o seu resultado deve ser especificada entre parênteses;
  2. Na utilização de pseudofunções, todos os argumentos devem ser obrigatoriamente especificados;
  3. O nome das pseudofunções é diferenciado pela utilização de letras maiúsculas ou minúsculas (case sensitive). Por exemplo, a pseudofunção RCUB foi definida, mas rcub não existe.

 

Pseudofunções devem ser utilizadas com cuidado e profissionalismo. Apesar de otimizarem o código executável, elas dificultarão a manutenção dos programas, pois possuem um escopo diferente das funções normais: são case sensitive e não permitem a omissão de argumentos. Por estes motivos, na maioria dos casos deve-se dar preferência à utilização de funções de usuário normais ou à definição de pseudofunções através da diretiva #translate. Neste último caso, as vantagens em termos de otimização do código são mantidas, a nomenclatura das pseudofunções seguirá as regras normais do AdvPL e argumentos poderão ser omitidos.

 

  • Sem rótulos