Retourner un type inconnu.

JegnuXJegnuX Membre
juillet 2012 modifié dans Objective-C, Swift, C, C++ #1
Salut,



Je suis en train de me faire une petite classe sympas dont je vous parlerais quand ce sera sur mon github image/tongue.png' class='bbc_emoticon' alt=':P' />.



Et en fait j'aurais besoin d'une méthode qui me retourne une valeur, mais dont je connais pas le type...



ça peut être aussi bien un objet (id), qu'un int, un float, un bool, un double, etc... À peu près n'importe quel type c (pas de tableau, pas de pointeurs, juste des types primitifs de base).





Bon déjà  j'ai fait deux méthode pour séparer les types C et les objet (id), afin de simplifier le truc.

Pour les objets du coup ça marche bien, y'avait pas de grande difficulté sur ce point.



Par contre, pour les types primitifs c'est plus compliqué. J'ai essayé de me baser sur le fonctionnement de NSValue, donc avec un passage de référence et en renseignant le type (avec @encode). Mais j'arrive pas à  ce que je veux.



La seule chose qui fonctionne, c'est en utilisant justement un NSValue. Mais ça me plait pas trop, j'aurais préféré comprendre le fonctionnement du NSValue en interne afin de pouvoir faire à  peu près la même chose.



Voilà  un peu le genre de code que j'ai (c'est un ptit projet de test que j'ai fais via CodeRunner) :


#import &lt;Foundation/Foundation.h&gt;<br />
<br />
@interface Test : NSObject<br />
<br />
@property (nonatomic, assign) const char *type;<br />
- (void) getValue:(void *)value fromString:(NSString *)string;<br />
@end<br />
<br />
@implementation Test<br />
@synthesize type;<br />
<br />
- (void) getValue:(void *)bufferValue fromString:(NSString *)string<br />
{<br />
    NSValue *valueObject;<br />
    if (strncmp(self.type, @encode(int), 1) == 0) {<br />
        int temp = [string intValue];<br />
        valueObject = [NSValue value:&amp;temp withObjCType:self.type];  //NSValue que j&#39;aimerais éviter<br />
        [valueObject getValue:bufferValue];<br />
    }<br />
    else if (strncmp(self.type, @encode(float), 1) == 0) {<br />
        float temp = [string floatValue];<br />
        valueObject = [NSValue value:&amp;temp withObjCType:self.type];  //NSValue que j&#39;aimerais éviter<br />
        [valueObject getValue:bufferValue];<br />
    }<br />
}<br />
<br />
@end<br />
<br />
int main(int argc, char *argv[]) {<br />
    @autoreleasepool {<br />
        Test *test = [Test new];<br />
<br />
        test.type = @encode(int);<br />
<br />
        int test_int;<br />
        float test_float;<br />
<br />
        [test getValue:&amp;test_int fromString:@&quot;42&quot;];<br />
        [test getValue:&amp;test_float fromString:@&quot;42&quot;];<br />
<br />
        NSLog(@&quot;%i ; %f&quot;, test_int, test_float);<br />
<br />
        test.type = @encode(float);<br />
<br />
        [test getValue:&amp;test_int fromString:@&quot;42&quot;];<br />
        [test getValue:&amp;test_float fromString:@&quot;42&quot;];<br />
<br />
        NSLog(@&quot;%i ; %f&quot;, test_int, test_float);<br />
    }<br />
}<br />




Avec ça j'obtiens grosso ce que je veux puisque ça me retourne
2012-07-26 14:32:30.623 Untitled 2[31459:707] 42 ; 0.000000<br />
2012-07-26 14:32:30.637 Untitled 2[31459:707] 1109917696 ; 42.000000<br />




Mais voilà , comment faire sans les NSValue ?

Réponses

  • AliGatorAliGator Membre, Modérateur
    C'est le
    valueObject = [NSValue value:&amp;temp withObjCType:self.type]; // idem<br />
                [valueObject getValue:bufferValue];
    que tu veux reproduire ?



    De ce que j'imagine, la méthode -[NSValue getValue:] ne fait que récupérer un "void*" de la bonne taille en octets. A toi ensuite de le caster dans le bon type. Pas besoin de passer par NSValue pour ça, une simple affectation suffit !

    Le but c'est de mettre dans l'emplacement mémoire pointé par le pointeur bufferValue, la valeur lue. Pour ça rien de plus simple :
    int temp = [string intValue];<br />
    *bufferValue = temp;
    Et c'est tout, pas besoin de plus. Faut juste pour pas que ça plante que tu passes un pointeur du bon type (ce que tu fais déjà , puisque tu passes &test_int pour lire un int, &test_float pour lire un float).



    Bon après, pour info, si tu as besoin de connaà®tre la taille que prend un type en mémoire à  partir de son Type Encoding, donc à  partir de @encode(int) savoir combien prend un int, tu as la fonction C Foundation NSGetSizeAndAlignment si ça peut te servir... même si dans ton cas je ne pense pas que ce soit utile.
  • Bah en fait ça peut mettre utile.



    Car là , oui je donne bien le bon type. Mais dans ma classe j'ai même pas le bon type ! Tu pourrais donc me montrer comment faire avec un type inconnu pour la variable passer par référence à  la méthode ?
  • JegnuXJegnuX Membre
    juillet 2012 modifié #4
    Bon et sinon si je remplace mes deux lignes de NSValue par ce que tu dis (que j'avais déjà  essayé), ça compile pas...


    <br />
    Untitled 2.m:18:16: error: incomplete type &#39;void&#39; is not assignable<br />
    				*bufferValue = temp;<br />
    				~~~~~~~~~~~~ ^<br />
    Untitled 2.m:22:16: error: incomplete type &#39;void&#39; is not assignable<br />
    				*bufferValue = temp;<br />
    				~~~~~~~~~~~~ ^<br />
    2 errors generated.<br />
  • HerveHerve Membre
    juillet 2012 modifié #5
    J'essaie une réponse, mais je n'ai pas testé.

    Avec des méthodes de C (puisque le problème vient des types C)
    <br />
    if (sizeof(maValeur) == sizeof(float)) {<br />
    //tralala...<br />
    }<br />
    else if ...<br />




    Mais je dis peut-être une grosse bêtise... Dans ce cas, désolé!
  • AliGatorAliGator Membre, Modérateur
    Oui c'est plutôt très dangereux ta solution Hervé.



    Car la taille d'un int et d'un float en mémoire est la même, donc sizeof(int) == sizeof(float). Baser ça condition là -dessus pour caster est donc dangereux !



    @JegnuX : Ah oui en effet, logique comme erreur. Mais bon, il suffit de caster ton pointeur pour le transformer de void* en int* ou float*, non ?
    if (strncmp(self.type, @encode(int), 1) == 0) {<br />
            int temp = [string intValue];<br />
            *((int*)bufferValue) = temp; // caster bufferValue de void* en int* avant d&#39;affecter la valeur vers laquelle il pointe<br />
        }<br />
        else if (strncmp(self.type, @encode(float), 1) == 0) {<br />
            float temp = [string floatValue];<br />
            *((float*)bufferValue) = temp; // caster bufferValue de void* en float* avant d&#39;affecter la valeur vers laquelle il pointe<br />
        }
    Bon j'ai pas testé (code tapé en live) mais ça devrait marcher image/wink.png' class='bbc_emoticon' alt=';)' />
  • Je risque de dire une connerie, mais Xcode peut compiler du C++ ?

    Il y a les templates C++ qui pourraient regler ce probleme non ?
  • Des que je démarre mon ladtop je vérifis ca mais j'ai fais ca déja en C il me semble.

    Si ca peu aider... je distribuerai sans souci.

    Cétait un exo d'ecole.



    Par-contre moi j'avais je me rappel que cétait beaucoup plus long comme fonction vu que je n'avais pas le droit a aucun appel syst.

    Je regarde ca des que j'ai la main dessus.
  • Merci ça à  tous. Muskvk, je veux bien voir ça oui.



    Mais sinon dans mon example sur coderunner, la solution d'Ali semble fonctionner.



    Faut juste que j'essaie de l'intégrer au vrai contexte qui est un poil plus compliqué.
  • AliGatorAliGator Membre, Modérateur
    juillet 2012 modifié #10
    Et dans le cas un peu particulier où tu as un type inconnu, tu peux toujours copier les octets représentant la valeur de temp dans bufferValue, avec un memcpy(bufferValue, &temp, sz) où sz est le nombre d'octets à  copier, donc la taille de ton type finalement, obtenu par exemple avec le NSGetSizeAndAlignment cité précédemment.



    Sauf que bon, si tu as un type inconnu, je me demande bien comment tu vas calculer ta valeur de "temp", là  où dans ton exemple quand tu sais que c'est un int tu prend [string intValue] et si c'est un float tu prends [string floatValue]... dans le cas générique je sais pas ce que tu comptes prendre image/tongue.png' class='bbc_emoticon' alt=':P' />
  • JegnuXJegnuX Membre
    juillet 2012 modifié #11
    'AliGator' a écrit:
    Sauf que bon, si tu as un type inconnu, je me demande bien comment tu vas calculer ta valeur de "temp", là  où dans ton exemple quand tu sais que c'est un int tu prend [string intValue] et si c'est un float tu prends [string floatValue]... dans le cas générique je sais pas ce que tu comptes prendre image/tongue.png' class='bbc_emoticon' alt=':P' />




    Dans mon cas précis, c'est le runtime qui me dis quoi faire. En gros je fait de l'introspection sur les Ivar et @property.
Connectez-vous ou Inscrivez-vous pour répondre.