2008-09-15 8 views
3

J'ai un code avec plusieurs fonctions très similaires les unes aux autres pour rechercher un élément dans une liste en fonction du contenu d'un champ dans une structure. La seule différence entre les fonctions est le type de la structure dans laquelle se trouve la recherche. Si je pouvais passer dans le type, je pourrais supprimer toute la duplication de code.Y a-t-il un moyen de passer un type de structure à une fonction c

J'ai aussi remarqué qu'il ya un certain verrouillage mutex qui se passe dans ces fonctions, donc je pense que je pourrais les laisser seuls ...

Répondre

0

Une façon de le faire est d'avoir un champ de type comme le premier octet de la structure. Votre fonction de réception examine cet octet, puis place le pointeur sur le type correct en fonction de ce qu'il découvre. Une autre approche consiste à transmettre les informations de type en tant que paramètre distinct à chaque fonction qui en a besoin.

3

Puisque les structures ne sont rien de plus que des blocs de mémoire prédéfinis, vous pouvez le faire. Vous pouvez passer un vide * à la structure, et un entier ou quelque chose pour définir le type. De là, le plus sûr chose à faire serait de refondre le void * dans un pointeur du type approprié avant d'accéder aux données.

Vous devez être très, très prudent, car vous perdez la sécurité de type lorsque vous transtypez vers un void * et vous risquez de vous retrouver avec une erreur d'exécution difficile à résoudre lors d'une opération de ce type.

-1

Je suis un peu rouillé sur c, mais essayez d'utiliser un pointeur void * comme type de variable dans le paramètre function. Passez ensuite l'adresse de la structure à la fonction, puis utilisez-la comme vous le feriez.

void foo(void* obj); 

void main() 
{ 
    struct bla obj; 
    ... 
    foo(&obj); 
    ... 
} 

void foo(void* obj) 
{ 
    printf(obj -> x, "%s") 
} 
+0

Il n'y a aucun moyen que cela compile jamais. Vous ne pouvez pas déréférencer un pointeur pour annuler comme ça. –

+0

Eh bien, pour être juste, j'ai dit que j'étais rouillé en C.;) –

6

Si vous vérifiez que le champ est placé au même endroit dans chaque structure, vous pouvez simplement jeter un pointeur pour obtenir sur le terrain. Cette technique est utilisée dans beaucoup de bibliothèques système de bas niveau, par ex. Prises BSD.

struct person { 
    int index; 
}; 

struct clown { 
    int index; 
    char *hat; 
}; 

/* we're not going to define a firetruck here */ 
struct firetruck; 


struct fireman { 
    int index; 
    struct firetruck *truck; 
}; 

int getindexof(struct person *who) 
{ 
    return who->index; 
} 

int main(int argc, char *argv[]) 
{ 
    struct fireman sam; 
    /* somehow sam gets initialised */ 
    sam.index = 5; 

    int index = getindexof((struct person *) &sam); 
    printf("Sam's index is %d\n", index); 

    return 0; 
} 

Vous perdez la sécurité du type en faisant cela, mais c'est une technique précieuse.

[J'ai maintenant testé le code ci-dessus et corrigé les diverses erreurs mineures. C'est beaucoup plus facile quand vous avez un compilateur. ]

+0

Il faut faire attention à ce qu'il n'y ait pas d'emballage de structure en cours. Un type de 'char' en tant que 1er champ d'une structure peut ne pas être stocké avec le même décalage dans une structure différente – itj

+0

La structure (sauf si vous demandez explicitement le contraire) des structures est dictée par la plateforme ABI. Je suppose que ce n'est pas illégal (comme c'est interdit par la norme ISO C) d'avoir un ABI qui effectue le genre de re-ordering et d'emballage dont vous parlez par défaut, mais ce serait assez fou. Je pense que beaucoup de logiciels existants ne fonctionneront tout simplement pas sur une telle plate-forme. – tialaramex

0

Vous pouvez le faire avec une macro paramétrée, mais la plupart des politiques de codage seront désapprouvées.


#include 
#define getfield(s, name) ((s).name) 

typedef struct{ 
    int x; 
}Bob; 

typedef struct{ 
    int y; 
}Fred; 

int main(int argc, char**argv){ 
    Bob b; 
    b.x=6; 

    Fred f; 
    f.y=7; 

    printf("%d, %d\n", getfield(b, x), getfield(f, y)); 
} 
0

Courte réponse: non. Vous pouvez toutefois créer votre propre méthode pour ce faire, c'est-à-dire fournir une spécification pour la création d'une telle structure. Cependant, ce n'est généralement pas nécessaire et ne vaut pas l'effort; juste passer par référence. (callFuncWithInputThenOutput(input, &struct.output);)

1

Je pense que vous devriez regarder les fonctions standard C qsort() et bsearch() pour vous inspirer. Il s'agit d'un code général pour trier les tableaux et rechercher des données dans un tableau pré-trié. Ils fonctionnent sur n'importe quel type de structure de données - mais vous leur passez un pointeur vers une fonction d'assistance qui effectue les comparaisons. La fonction d'aide connaît les détails de la structure et effectue donc la comparaison correctement. En fait, puisque vous voulez faire des recherches, il se peut que vous ayez besoin de bsearch(), mais si vous construisez les structures de données à la volée, vous pouvez décider que vous avez besoin d'une structure différente de celle d'un tri. liste. (Vous pouvez utiliser des listes triées - cela a simplement tendance à ralentir les choses par rapport à, disons, un tas.Cependant, vous auriez besoin d'une fonction générale heap_search(), et d'une fonction heap_insert(), pour faire le travail correctement, et ces fonctions ne sont pas standardisées en C. Rechercher sur le web montre que de telles fonctions existent - pas par ce nom; n'essayez pas "c heap search" car on suppose que vous avez voulu dire "recherche bon marché" et vous obtenez des tonnes de déchets)

1

Si le champ ID que vous testez fait partie d'une séquence initiale commune de champs partagés par tous struct, puis à l'aide d'un syndicat garantit que l'accès fonctionne:

#include <stdio.h> 

typedef struct 
{ 
    int id; 
    int junk1; 
} Foo; 

typedef struct 
{ 
    int id; 
    long junk2; 
} Bar; 

typedef union 
{ 
    struct 
    { 
     int id; 
    } common; 

    Foo foo; 
    Bar bar; 
} U; 

int matches(const U *candidate, int wanted) 
{ 
    return candidate->common.id == wanted; 
} 

int main(void) 
{ 
    Foo f = { 23, 0 }; 
    Bar b = { 42, 0 }; 

    U fu; 
    U bu; 

    fu.foo = f; 
    bu.bar = b; 

    puts(matches(&fu, 23) ? "true" : "false"); 
    puts(matches(&bu, 42) ? "true" : "false"); 

    return 0; 
} 

Si vous êtes malchanceux, et le champ apparaît à différents décalages dans les différents struct, vous pouvez ajouter un paramètre de décalage à votre fonction. Ensuite, offsetof et une macro d'encapsulation simulent ce que l'OP a demandé - en passant le type de struct sur le site d'appel:

#include <stddef.h> 
#include <stdio.h> 

typedef struct 
{ 
    int id; 
    int junk1; 
} Foo; 

typedef struct 
{ 
    int junk2; 
    int id; 
} Bar; 

int matches(const void* candidate, size_t idOffset, int wanted) 
{ 
    return *(int*)((const unsigned char*)candidate + idOffset) == wanted; 
} 

#define MATCHES(type, candidate, wanted) matches(candidate, offsetof(type, id), wanted) 

int main(void) 
{ 
    Foo f = { 23, 0 }; 
    Bar b = { 0, 42 }; 
    puts(MATCHES(Foo, &f, 23) ? "true" : "false"); 
    puts(MATCHES(Bar, &b, 42) ? "true" : "false"); 

    return 0; 
}