Fonctions d'extension C ++?

Existe-t-il des extensions pour C ++ comme en C #?

Par exemple, en C#, vous pouvez faire:

public static uint SwapEndian(this uint value)
{
    var tmp = BitConverter.GetBytes(value);
    Array.Reverse(tmp);
    return BitConverter.ToUInt32(tmp, 0);
}

someuint.SwapEndian();

Y at-il quelque chose comme ça en C ++?

13
Non, C ++ n'a rien de tel. Autant que je sache, il n'y a aucune proposition pour C ++ 17 à ce sujet non plus (écrivez-en une!). Le langage de programmation D a UFCS (syntaxe d'appel de fonction uniforme), qui correspond exactement aux "méthodes d'extension". Ce serait une fonctionnalité intéressante, car vous pouvez écrire des fonctions non membres non membres et éviter le paramètre object, ce qui donne un code pouvant être lu de gauche à droite au lieu de "entrée-à-sortie/droite-à-entrée". la gauche".
ajouté l'auteur gnzlbg, source
C’est l’une des nombreuses raisons pour lesquelles C ++ ne peut plus être considéré comme un langage de programmation valide. Il est trop désuet, manque de nombreuses fonctionnalités des langages modernes comme le C# et est le contraire de productif. Microsoft doit recommencer à zéro avec un nouveau langage compilé qui ressemble à C #/Java. Si quelqu'un me dit qu'il utilise le C ++, je me moque d'eux et je le vois immédiatement comme étant moins intelligent que moi.
ajouté l'auteur Krythic, source

7 Réponses

Il n'y a pas de fonctions d'extension en C ++. Vous pouvez simplement les définir en tant que fonctions libres.

uint SwapEndian(uint value){ ... }
5
ajouté
C'est la seule réponse qui utilise le code d'origine. Je vous remercie. Il montre également un exemple de ce que l’on entend par fonctions libres et la plupart des réponses ici et ailleurs ne le font pas. Certaines réponses dans d'autres discussions sur ce sujet donnent une longue description philosophique de la POO et de son fonctionnement supposé, mais manquent de détails utiles.
ajouté l'auteur user34660, source

Les méthodes d'extension (et aussi les "classes statiques") existent dans les langages C #/Java uniquement parce que les concepteurs ont décidé que (la méthode Java) la POO est la méthode la plus vraie et que tout doit être une méthode d'une classe:

Ce n'est pas une façon de faire les choses en C ++. En C ++, vous avez des espaces de noms, des fonctions libres et un recherche Koenig pour étendre le comportement d'une classe:

namespace foo
{
    struct bar { ... };

    void act_on_bar(const bar& b) { ... };
}

...

foo::bar b;
act_on_bar(b);//No need to qualify because of Koenig lookup

Je considère généralement les méthodes d'extension nuisibles. Si vous attachez trop de comportement à une classe, vous ne parvenez probablement pas à saisir la raison pour laquelle la classe existe. De plus (comme les "classes partielles"), ils ont tendance à rendre le code lié à une classe non locale. Ce qui est mauvais

En ce qui concerne votre problème, en C ++, vous faites simplement:

template 
T swap_endian(T x)
{
    union { T value; char bytes[sizeof(T)]; } u;
    u.value = x;

    for (size_t i = 0; i < sizeof(T)/2; i++) 
        swap(u.bytes[i], u.bytes[sizeof(T) - i - 1]);

    return u.value;
}

Usage:

swap_endian(42);

ou, si le type peut être déduit:

std::uint64_t x = 42;
std::uint64_t y = swap_endian(x);
5
ajouté
@gnzlbg, d'accord. Elles étaient parfois très pratiques en C#, cependant les méthodes d’extension de style C# prenaient toujours this comme non-const T & (référence). Dans le cas où b (a) équivaut à ab() , le premier argument de b doit-il être un pointeur ou une référence ? Serait-il jamais logique de copier? J'aimerais que cette syntaxe soit disponible en C ++ et je me demande s'il existe des obstacles à son introduction, mis à part le désintérêt de la communauté et du comité.
ajouté l'auteur Drew Noakes, source
Les méthodes d'extension ne sont que du sucre syntaxique. Vous pouvez avoir une fonction libre act_on_bar et l'utiliser comme act_on_bar (b) ou b.act_on_bar() . La deuxième manière n’a rien à faire avec la vraie méthode OO , mais uniquement avec la lisibilité. Par exemple. si vous avez 2 fonctions; act2 (act1 (b)) est moins lisible que b.act1 (). act2() . En C ++, vous devez créer des fonctions membres act1 et act2 pour obtenir du code lisible, même s'il serait plus logique de les utiliser en tant que fonctions libres. ! Cela améliorerait également la programmation générique. Et lisible n’est pas subjectif ici, car la plupart des gens lisent le texte de gauche à droite, mais pas de bout en bout.
ajouté l'auteur gnzlbg, source
Mais le principe peut sûrement être appliqué sans classes. Pour que Brut C lie des fonctions à des classes sans les forcer dans une structure de classe en croissance constante. Je veux C où je peux lier des fonctions à des structures ou à des primitives sans couplage ni mutilation incontrôlée (puisque je me lie, il n'y a pas de mutilation, car elle est traduite au moment de la compilation). Les abstractions libres sans fuite d’abstraction dans les fichiers binaires résultants sont belles.
ajouté l'auteur Dmitry, source

Ce n'est pas comme ça, mais vous pouvez écrire des surcharges d'opérateur qui fonctionnent sur des classes que vous n'avez pas écrites, et c'est un peu comme les extensions de méthode (mais pas pour les fonctions nommées, mais uniquement pour les opérateurs déjà défini par cette classe). L'exemple classique consiste à faire fonctionner votre classe avec cout :

class MyClass {
public:
    MyClass(const char* blah) : str(blah) { }

    const char* string() const {
        return str;
    }

private:
    const char* str;
};

// this is kinda like a method extension
ostream& operator<<(ostream& lhs, const MyClass& rhs) {
    lhs << rhs.string();
}

// then you can use it like this
MyClass m("hey ho");
cout << m;

// prints hey ho

Ceci est un exemple trivial, bien sûr, mais vous avez l’idée.

3
ajouté

Pas de manière directement analogue, mais vous pouvez souvent obtenir l'effet souhaité à l'aide de modèles. Vous ne pouvez pas "ajouter" de méthodes à une classe concrète en C ++ sans dériver de la classe d'origine, mais vous pouvez créer des modèles de fonction fonctionnant avec n'importe quel type.

Par exemple, voici une bibliothèque de modèles de fonctions que j'utilise pour effectuer des conversions de type ntoh de tout type intégral:

template inline Val ntohx(const Val& in)
{
    char out[sizeof(in)] = {0};
    for( size_t i = 0; i < sizeof(Val); ++i )
        out[i] = ((char*)&in)[sizeof(Val)-i-1];
    return *(reinterpret_cast(out));
}

template<> inline unsigned char ntohx(const unsigned char & v )
{
    return v;
}
template<> inline uint16_t ntohx(const uint16_t & v)
{
    return ntohs(v);
}

template<> inline uint32_t ntohx(const uint32_t & v)
{
    return ntohl(v);
}

template<> inline uint64_t ntohx(const uint64_t & v)
{
    uint32_t ret [] =
    {
        ntohl(((const uint32_t*)&v)[1]),
        ntohl(((const uint32_t*)&v)[0])
    };
    return *((uint64_t*)&ret[0]);
}
template<> inline float ntohx(const float& v)
{
    uint32_t const* cast = reinterpret_cast(&v);
    uint32_t ret = ntohx(*cast);
    return *(reinterpret_cast(&ret));
};
2
ajouté
@ Alexandre: C'est la même chose que la tienne. La seule différence est que j'ai fourni des spécialisations pour les types intégrés pouvant être gérées par ntohs et ntohl .
ajouté l'auteur John Dibling, source
C'est une manière assez complexe d'accomplir la tâche à accomplir.
ajouté l'auteur Alexandre C., source
dans ntohx comment savez-vous que les deux valeurs 32 bits du tableau de retour sont dans le bon ordre? ;)
ajouté l'auteur Géza Török, source

Non, désolé, il n’ya rien de tel en C ++ et c’est impossible. Il y a beaucoup de choses que la norme laisse dépendantes de l'implémentation (le compilateur peut le faire comme bon lui semble), et C ++ n'a pas de standard ABI .

1
ajouté
@Sean: les méthodes d'extension doivent prendre en charge ce scénario: vous prenez les en-têtes d'une bibliothèque tierce A et votre propre code (bibliothèque B) qui définit et utilise les méthodes d'extension sur les types définis en A. Votre éditeur de liens doit alors Découvrez comment connecter les appels à un binaire de A éventuellement pré-construit, qui peut très bien avoir été produit par un autre compilateur/éditeur de liens . Vous ne pouvez jamais faire cela sans normalisation sur une ABI (ce qui ne se passera franchement pas à cause de pauses BC).
ajouté l'auteur Jon, source
@Sean: Pour le dire autrement: dans .NET, les assemblys doivent contenir des métadonnées extrêmement détaillées sur les types qu'ils contiennent. En C ++ OTOH, rien ne garantit qu’il existe quoi que ce soit dans un binaire qui puisse même indiquer qu’il contient une classe que vous avez définie, peu importe son nom et d’autres détails.
ajouté l'auteur Jon, source
Je pense que dire "ça ne peut jamais aussi être" est un peu déraisonnable. Ils pourraient ajouter une forme de mécanisme de méthode d'extension s'ils le voulaient, ils ont simplement choisi de ne pas le faire.
ajouté l'auteur Sean, source

One method I have found is to use the overloaded ">>" operator with lambda expressions. The following code demonstrates this. You have to know to use operator ">>" instead of "->", this is because the compiler I use will not allow the operator "->" to be overloaded. Also because the operator ">>" has lower precedence than the "->" you have to use parentheses to force to compiler to evaluate the equation in the correct order.

En fin de compte, cela devient une question de style, de facilité de maintenance, de fiabilité et de propreté du code que vous essayez de produire. On pourrait argumenter que la définition de la méthode "SubtractValue" avec deux arguments crée un code plus efficace, mais d'autres soutiendraient que la méthode surchargée est plus facile à gérer. En fin de compte, il appartient aux architectes et aux développeurs de déterminer ce qui est important pour leur projet. Je ne fais que proposer une solution possible au problème.

#include 
#include 
#include 
#include 

// Some plain demo class that cannot be changed.
class DemoClass
{
public:
    int GetValue() { return _value; }
    int SetValue(int ivalue) { _value = ivalue; return _value; }
    DemoClass *AddValue(int iadd) { this->_value += iadd; return this; }

private:
    int _value = 0;
};

// Define Lambda expression type that takes and returns a reference to the object.
typedef std::function DemoClassExtension;

// Overload the ">>" operator because we cannot overload "->" to execute the extension.
DemoClass* operator>>(DemoClass *pobj, DemoClassExtension &method)
{
    return method(pobj);
}

// Typical extensions.

// Subtract value "isub".
DemoClassExtension SubtractValue(int isub)
{
    return [=](DemoClass *pobj) {
        pobj->AddValue(-isub);
        return pobj;
    };
}

// Multiply value "imult".
DemoClassExtension MultiplyValue(int imult)
{
    return [=](DemoClass *pobj) {
        pobj->SetValue(pobj->GetValue() * imult);
        return pobj;
    };
}

int _tmain(int argc, _TCHAR* argv[])
{
    DemoClass *pDemoObject = new DemoClass();
    int value = (pDemoObject->AddValue(14) >> SubtractValue(4) >> MultiplyValue(2))->GetValue();
    std::cout << "Value is " << value;
    return 0;
}

Le code ci-dessus est "La valeur est 20".

1
ajouté

Si vous vous référez au paramètre de la méthode this -qualified, alors non. Mais il peut y avoir d'autres astuces astucieuses en fonction de votre cas d'utilisation spécifique ... Pouvez-vous fournir plus de détails?

1
ajouté