Ir ao conteúdo
  • Cadastre-se

C Struct com acesso limitado


Midori

Posts recomendados

Ao tentar pegar os valores diretamente de uma estrutura, me deparei com este erro na compilação,

 

Error : forward declaration of ‘Display

 

Este é o código fonte e a alinha apontada pelo compilador é onde tento mostrar display_name,

 

#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
    Display *display = XOpenDisplay(NULL);
    
    if(display == NULL){
        fprintf(stderr, "Erro ao tentar conectar no servidor X\n");
        exit(1);
    }
    
    puts(display->display_name);
    
    XCloseDisplay(display);
    return 0;
}

 

No header Xlib.h vi que isso é por causa de alguns defines que acredito ser uma forma de deixar a programar mais segura. Assim durante o projeto as informações são retornadas por funções e não por acesso direto aos campos da struct. No header tem estas linhas no inicio da declaração da struct Display,

 

typedef struct
#ifdef XLIB_ILLEGAL_ACCESS
_XDisplay
#endif
{
    XExtData *ext_data;	/* hook for extension to hang data */
    struct _XPrivate *private1;
    int fd;			/* Network socket. */

 

E no fim,

}
#ifdef XLIB_ILLEGAL_ACCESS
Display,
#endif
*_XPrivDisplay;

 

Achei isso interessante e tentei aplicar em outro projeto, mas o compilador mostra diversos erros como estes,

 

In file included from main.c:2:

ponto.h:16:7: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘*’ token

   16 | Ponto *inicia(int, int);

        | ^ ponto.h:17:10: error: unknown type name ‘Ponto’ 17 | int getx(Ponto *); | ^~~~~

ponto.h:18:10: error: unknown type name ‘Ponto’ 18 | int gety(Ponto *);

...

 

Este é o meu código de teste,

 

Qual é melhor forma de aplicar esse método?

 

Tem que colocar diversos defines no ponto.c? No caso #define ACESSO_ILEGAL

 

main.c

#include <stdio.h>
#include "ponto.h"

int main(int argc, char *argv[]){
    Ponto *ponto = inicia(10, 20);
    
    printf("%d, %d\n", ponto->x, ponto->y);
    
    printf("%d\n", getx(ponto));
    
    return 0;
}

 

ponto.c

#include <stdlib.h>
#include "ponto.h"

Ponto *inicia(int x, int y){
    Ponto *ini = (Ponto *)malloc(sizeof(Ponto)); 
    ini->x = x;
    ini->y = y;
    return ini;
}

int getx(Ponto *p){
    return p->x;
}

int gety(Ponto *p){
    return p->y;
}

 

ponto.h

#ifndef PONTO_H
#define PONTO_H

typedef struct 
#ifdef ACESSO_ILEGAL 
_Ponto
#endif
{
    int x;
    int y;
}
#ifdef ACESSO_ILEGAL
Ponto;
#endif

Ponto *inicia(int, int);
int getx(Ponto *);
int gety(Ponto *);

#endif

 

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

Talvez não seja a melhor forma, mas com #define ACESSO_ILEGAL na primeira linha de ponto.c deu certo.

 

Removi as funções int e no lugar coloquei macros, o header ficou assim,

 

ponto.h

#ifndef PONTO_H
#define PONTO_H

#define getx(ponto) (((_PtrPonto)(ponto))->x)
#define gety(ponto) (((_PtrPonto)(ponto))->y)

#ifndef ACESSO_ILEGAL
    typedef struct _Ponto Ponto;
#endif

typedef struct 
    #ifdef ACESSO_ILEGAL 
        _Ponto
    #endif
{
    int x;
    int y;
}
    #ifdef ACESSO_ILEGAL
        Ponto,
    #endif
*_PtrPonto;

Ponto *inicia(int, int);

#endif

 

Dessa forma se for feita a tentativa de pegar os valores direto da struct vai dar o erro,

printf("%d, %d\n", ponto->x, ponto->y);

main.c: In function ‘main’:
main.c:7:29: error: dereferencing pointer to incomplete type ‘Ponto’ {aka ‘struct _Ponto’}
    7 |     printf("%d, %d\n", ponto->x, ponto->y);
      |         

 

Mas assim não,

printf("%d, %d\n", getx(ponto), gety(ponto));

 

Claro que é possível colocar aquele define em main, mas não seria uma boa prática.

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

X11 provavelmente não seria um lugar para buscar boas práticas no século 21.Trata-se de código dos anos 70/80.

 

Esse é o comentário em Xlib.h sobre essa "técnica" 
 

/*
 * Display datatype maintaining display specific data.
 * The contents of this structure are implementation dependent.
 * A Display should be treated as opaque by application code.
 */
#ifndef XLIB_ILLEGAL_ACCESS
    typedef struct _XDisplay Display;
#endif

 

E o uso da "técnica"

 

typedef struct
#ifdef XLIB_ILLEGAL_ACCESS
_XDisplay
#endif
{
	XExtData *ext_data;	/* hook for extension to hang data */
	struct _XPrivate *private1;  
  // resto das variaveis
  /* there is more to this structure, but it is private to Xlib */
}
#ifdef XLIB_ILLEGAL_ACCESS
Display,
#endif
  
  *_XPrivDisplay;


Assim se vê que se habilitar o assustador XLIB_ILLEGAL_ACCESS_ a estrutura deixa de ser anônima. Nada mais

 

E veja que provavelmente é melhor assim 🙂 Eu não copiei o resto das variáveis mas veja que tem um ponteiro para dados adicionais, que está lá desde os anos 80, ext_data.  Mais ponteiros para struct _XPrivate  e acho que contei uns 20 deles. Isso é uma gambiarra ajustada por décadas.

 

E note que hoje em dia muita gente --- empresas e escolas --- sequer permite a criação de ponteiros via typedef, que é uma coisa tosca, já que esse é o significado do asterisco. Onde seria vantagem? _XPrivDisplay é um ponteiro? Como saber? Tem que ler a definição toda hora. Tudo bem, são mais de 40 anos.

 

De volta a ponto.h ponto.c

 

Em 10/11/2022 às 09:46, Midori disse:

Qual é melhor forma de aplicar esse método?

 

Acho melhor esquecer esse método

 

Em 10/11/2022 às 09:46, Midori disse:

Tem que colocar diversos defines no ponto.c? No caso #define ACESSO_ILEGAL

 

Veja em seu compilador qual a opção, mas sempre tem uma maneira de mostrar o código expandido. A maneira que usou é pouco recomendável porque é muito difícil de ler, como era nos anos 80 em xlib.

 

Seria melhor usar um único ifdef e colocar o código completo lá dentro ao invés de deixar tudo picado.

 

No caso do MSVS a opção para ver o código gerado seria

 

        CL /c /P /DACESSO_ILEGAL ponto.c

 

/P gera um arquivo ponto.i com o código expandido.

 

/D é o define

 

/c é pra não compilar o código afinal, já que só se quer a expansão. Pode nem ser necessário

 

No seu exemplo em ponto.h

 

typedef struct
#ifdef ACESSO_ILEGAL
    _Ponto
#endif
{
    int x;
    int y;
}

#ifdef ACESSO_ILEGAL
Ponto;
#endif

Ponto* inicia(int, int);
int    getx(Ponto*);
int    gety(Ponto*);

 

é muito ruim de ler. E é tão ruim que acaba escondendo um erro. Compare com o simples:

 

#ifdef ACESSO_ILEGAL

typedef struct _Ponto
{
    int x;
    int y;
}   Ponto;

#else

typedef struct
{
    int x;
    int y;
}
#endif

 

E assim fica fácil de ver que se ACESSO_ILEGAL não estiver definido não tem o ';' no final da struct anônima e assim o compilador acaba vendo isso como uma declaração de inicia() retornando  um ponteiro para uma struct anônima chamada Ponto.

 

Talvez você não tenha percebido que em xlib.h
 

#ifdef XLIB_ILLEGAL_ACCESS
    Display,
#endif
    *_XPrivDisplay;

 

DOIS nomes são declarados ou apenas um. No seu caso ficou só 1 e por isso dá erro.

 

4 horas atrás, Midori disse:

Talvez não seja a melhor forma, mas com #define ACESSO_ILEGAL na primeira linha de ponto.c deu certo.

 

Não, não é uma boa forma. Não "deu certo". Apenas compilou e você tem um #define emque o código só compila se estiver presente. De que serviria isso?

 

Apenas está errado porque só tem um nome. Se quer usar aquela ideia infeliz de xlib.h entenda que ao definir ACESSO_ILEGAL deve criar UM PAR de novas entidades. Um nome para a struct e um nome abaixo na declaração.

 

Para ACESSO_ILEGAL definido seu código seria
 


typedef struct _Ponto
{
    int x;
    int y;
}   Ponto;


Ponto* inicia(int, int);
int    getx(Ponto*);
int    gety(Ponto*);

 

E sem esse valor definido

 

typedef struct
{
    int x;
    int y;
}   Ponto* inicia(int, int);

int    getx(Ponto*);
int    gety(Ponto*);

 

E acho que dá para entender melhor. Compare com Xlib.h e verá onde errou.

 

escrever algo para X_Windows sempre foi um desastre. Se não me engano a própria X.org Foundation editava um guia de OITO volumes como referência para o software da plataforma. Eu tenho um por aqui em algum lugar.

 

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

Em 11/11/2022 às 18:34, arfneto disse:

Não, não é uma boa forma. Não "deu certo". Apenas compilou e você tem um #define emque o código só compila se estiver presente. De que serviria isso?

No caso da Xlib também é possível compilar sem o erro que comentei no primeiro post com define XLIB_ILLEGAL_ACCESS em main. A questão é, se entendi o propósito disso, desencorajar o acesso direto aos campos da struct. Me parece uma forma de deixar os membros "privados" e evitar atribuições indevidas em projetos que fazem uso da biblioteca. Assim o acesso só seria devidamente feito em algum momento da programação da lib (libx11-dev ou libponto-dev por exemplo) para ser distribuída com esse tipo de restrição.

 

Em 11/11/2022 às 18:34, arfneto disse:

é muito ruim de ler. E é tão ruim que acaba escondendo um erro. Compare com o simples:

 

Assim ficou melhor mesmo.

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

1 hora atrás, Midori disse:

No caso da Xlib também é possível compilar sem o erro que comentei no primeiro post com define XLIB_ILLEGAL_ACCESS em main

 

O propósito desses #define é permitir compilar o código de mais de uma maneira mudando apenas o valor dessa variável.  Não pode "compilar sem erro" porque tirou ou colocou um #define. No seu caso estava errado pela falta do ';' 

 

X-Windows era parte de um software proprietário. Isso nunca foi pensado em ser distribuído, nem o Unix. Isso foi meio que roubado da AT&T que tinha métodos e custos de licenciamento bem restritivos. E aí temos o "movimento Open Source" e nasceu o Linux, o MACOS, o Android...

 

De volta ao Xlib

 

Veja _XExtData por exemplo: 

 

/*
 * Extensions need a way to hang private data on some structures.
 */
typedef struct _XExtData {
	int number;		/* number returned by XRegisterExtension */
	struct _XExtData *next;	/* next item on list of data for structure */
	int (*free_private)(	/* called to free private storage */
	struct _XExtData *extension
	);
	XPointer private_data;	/* data private to this extension. */
} XExtData;

 

_XExtData é uma linked list de estruturas que são numeradas e registradas por número em algum canto (XRegister) e dentro delas tem dúzias de XPointer chamados privateNN. 

 

Ninguém quer desenvolver em um ambiente desses. Hoje seria só proibido. Fuja disso.

 

Quer private access em C? Use void*[] e não diga pra ninguém o que tem dentro.

 

1 hora atrás, Midori disse:

Me parece uma forma de deixar os membros "privados" e evitar atribuições indevidas em projetos que fazem uso da biblioteca. Assim o acesso só seria devidamente feito em algum momento da programação da lib (libx11-dev ou libponto-dev por exemplo) para ser distribuída com esse tipo de restrição

 

Sim, esse é o propósito. Mas vale a pena? 

 

 

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

Ebook grátis: Aprenda a ler resistores e capacitores!

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!