简体   繁体   中英

C++ Extension functions?

Are there extensions for C++ like there are in C#?

For example in C# you can do:

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

someuint.SwapEndian();

Is there anything like that in C++?

Extension methods (and also "static classes") exist in C#/Java languages solely because the designers decided that (the Java way of) OOP is The One True Way and that everything must be a method from a class:

This is not C++ way of doing things. In C++ you have namespaces, free functions and Koenig lookup to extend the behavior of a class:

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

I usually consider extension methods harmful. If you attach too much behavior to a class, you are proabably failing to capture the reason why the class exists. Also (like "partial classes"), they tend to make the code related to a class non local. Which is bad.

As to your problem, in C++ you simply do:

template <typename T>
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<std::uint32_t>(42);

or, if the type can be deduced:

std::uint64_t x = 42;
std::uint64_t y = swap_endian(x);

There are no extension functions in C++. You can just define them as free functions.

uint SwapEndian(uint value){ ... }

Not like that, but you can write operator overloads which work on classes you didn't write, and it's a little like like method extensions (but not for named functions, only for operators that haven't been defined by that class already). The classic example is making your class work with 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

This is a trivial example of course, but you get the idea.

Not in a directly-analogous way, but many times you can achieve the desired effect using templates. You cannot "add" methods to a concrete class in C++ without deriving from the original class, but you can create function templates that work with any type.

For example, here's a function template library I use to do ntoh-type conversions of any integral type:

template<class Val> 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<Val*>(out));
}

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

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

template<> inline uint64_t ntohx<uint64_t>(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<float>(const float& v)
{
    uint32_t const* cast = reinterpret_cast<uint32_t const*>(&v);
    uint32_t ret = ntohx(*cast);
    return *(reinterpret_cast<float*>(&ret));
};

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.

In the end it becomes a matter of style, maintainability, reliability and cleanness of the code you are trying to produce. One would argue defining the "SubtractValue" method with two arguments creates more efficient code, but others would argue the overloaded method is more maintainable. In the end it is left to the architects and developers to determine what is important to their project. I am just providing a possible solution to the issue.

#include <functional>
#include <iostream>
#include <stdio.h>
#include <tchar.h>

// 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<DemoClass *(DemoClass *obj)> 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;
}

The above code output is "Value is 20".

No, sorry, but there's nothing like that in C++ and it can also never be. There are lots of things that the standard leaves as implementation-dependent (ie the compiler can do it any way it prefers), and also C++ does not have a standardized ABI .

If you're referring to the this -qualified method parameter, then no. But there may be some other clever tricks depending on your specific use case... Can you provide more detail?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM