Newsgroup: it.comp.lang.c
Titolo: allocazione vettori di tipo diverso con una sola funzione

Sto cercando di scrivere una serie di funzioni per la gestione dei vettori
di diverso tipo. 
Il problema che non riesco a risolvere è come creare una struttura generica
vector che accetti qualsiasi tipo, int *val originariamente
era un void *val e solo che con le informazioni riportate nella struttura
non sono riuscito a creare una macro per fare il cast al tipo desiderato.
Potete darmi una traccia da seguire?
TIA
 
struct __arrays_prop__ {
                        int dimX;
                        int dimY;
                        int dimZ;
                        int dimT;
                        char type;
                        int size;
                       } ;
struct __vector__ {
                        int *val;
                        struct __arrays_prop__ p;
                  };
typedef struct __vector__ vector;
void vect_init(vector *x)
{
        x->val[0] = 0;
        x->p.dimX = 0;
        x->p.dimY = 0;
        x->p.dimZ = 0;
        x->p.dimT = 0;
        x->p.type = '\0';
        x->p.size = 0;
}
void vect_allocate(vector *x, int lenX, int lenY, int lenZ, int lenT, char tp)
{
	if ( (lenX >0) && (lenY == -1) && (lenZ == -1) && (lenT == -1) )
	{
   	     if (tp == 'i')
   	     {
    	   	      x->p.size = sizeof(int);
  	              x->p.type = 'i';
	              x->val = malloc(lenX * x->p.size);
 	     }
             x->p.dimX = len;
	}
}
void vect_deallocate(vector *x)
{
        free(x->val);
}

non sono riuscito a creare una macro per fare il cast al tipo desiderato.
Dipende da come vuoi vedere tu la cosa.
Una delle soluzioni piu' generiche e' quello di dichiarare (in modo
opportuno) una generica ADT vector che non e' null'altro se non un
array di void *.
In questo modo la ricicli senza fatica per qualsiasi cosa, dagli
interi alle strutture con 40 campi.
Visto che pero' in molti casi potrebbe essere comodo implementare un
vettore di int senza passare per i void * ti fai delle macro che
convertano da VOID * a INT (il trucco usato nelle Glib per usare l'ADT
lista come lista di interi in modo diretto, quindi puoi spulciare i
sorgenti per capire come funziona la macro), ovviamente ammesso che la
dimensione dei puntatori sia la stessa degli interi.
Per il cast vedo 3 modi (sono i primi 3 che mi sono venuti in mente):
- lo lasci fare all'utente che se lo fa' nel modo migliore e per il
tipo di dato che usa effettivamente
- ne fornisci una serie tu
- fornisci una macro generica che fa' il cast al tipo che gli viene
specificato
Il vector te lo fai come una struttura dati che contenga
effettivamente un void *, un int che tiene traccia della dimensione
effettiva (magari potresti farlo che si rialloca automaticamente della
dimensione giusta, il che comporta anche indicatore della dimensione
allocata).
Per tener traccia del tipo di ogni cella del vettore nella struttura e
farlo in modo da supportare qualsiasi tipo che l'utente si possa
iventare... beh, non e' facile. Sinceramente non mi viene in mente un
modo (che non sia un flag che tiene conto di alcuni casi).
Mox :)

 struct __arrays_prop__ {
         ^^^^^^^^^^^^^^^
Gli  identificatori  che  contengono  un doppio  underscore  oppure  che
iniziano  con un  underscore  snon riservati  per  l'uso nella  libreria
standard.
Per la lettura ti potresti appoggiare ad una  funzione di questo tipo:
/* Per comodita` ridefinisco la struttura vettore */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
        void *data;
        size_t type_size;
	int dim_max;
} vector_t;
#define v_init(v,dim,type) v_init_(v, dim, sizeof(type))
#define v_get(buff,v,offset) v_get_(buff, sizeof(*buff), v, offset)
#define v_set(buff,v,offset) v_set_(buff, sizeof(*buff), v, offset)
int v_get_(void *buff, size_t buff_size, vector_t v, int offset) {
        char *p;
        if (offset > v.dim_max)
                return -1;
	if (buff_size != v.type_size) {
		printf("buff_size != v.type_size\n");
		return -1;
	}
        p = v.data;
        p += offset * v.type_size;
        memcpy(buff, p, v.type_size);
	return 0;
}
int v_set_(void *buff, size_t buff_size, vector_t v, int offset) {
	char *p;
	if (offset > v.dim_max)
		return -1;
	if (buff_size != v.type_size) {
		printf("buff_size != v.type_size\n");
		return -1;
	}
	p = v.data;
	p += offset * v.type_size;
	memcpy(p, buff, v.type_size);
	return 0;
}
void v_init_(vector_t *v, int dim, size_t size) {
	v->type_size = size;
	v->dim_max = dim;
	v->data = malloc(size * dim);
}
v_free(vector *v) {
	free(v->data);
	/* Queste dovrebbero prevenire l'accesso ad un vettore vuoto */
	v->max_dim = 0;
	v->type_size = 0;
}
void foobar(void) {
	vector_t v;
	double d;
	int i;
	v_init(&v, 15, double);
	d = 3.14;
 
	v_set(&d, v, 10);
	v_get(&d, v, 10);
	/* Questo da un errore */
	v_get(&i, v, 10);
	v_free(&v);
}
Il campo type_size contiene la dimensione (quanti char) del tipo di dato
che e` contenuto nell'array. In questo modo, data la posizione (offset),
posso recuperare  il dato. L'inconveniente e` che  l'utente deve passare
come prima argomento  di set/get un tipo di dato  compatibile con quello
che  contiene il  vettore. E`  possibile effettuare  questo controllo  a
runtime  con l'uso  di una  macro, tuttavia  il controllo  non e`  molto
accurato (nessuna promozione dei tipi) e viene eluso passando un array:
long l;
char c[sizeof(int)];
v_init(&v, 10, int);
/* Questo potrebbe essere sensato, ma non passa il controllo */
v_get(&l, v, 4);
/* Questo non e` sensato, ma passa il controllo */
v_get(&c, v, 4);
ciao
Kronos
-- 
Home: 
http://kronoz.cjb.net
Il piu` bel momento dell'amore e` quando ci si illude che duri
per sempre; il piu` brutto, quando ci si accorge che dura
da troppo.

grazie ad entrambi:)