开发者

C++ Class Extension

开发者 https://www.devze.com 2022-12-09 15:35 出处:网络
Is there a way to add new methods to a class, without modifying original class definitio开发者_Python百科n (i.e. compiled .lib containing class and corresponding .h file) like C#\'s class extension me

Is there a way to add new methods to a class, without modifying original class definitio开发者_Python百科n (i.e. compiled .lib containing class and corresponding .h file) like C#'s class extension methods?


No. C++ has no such capability.

As mentioned in other answers, the common workarounds are:

  • Define a derived class, perhaps with a factory to hide the actual implementation class
  • Define a decorator class
  • Define non-member functions that operate on instances of the class


No, you can't do this in C++.

If you want to achieve something like this you have 2 options,

  • You could inherit from the class (if this is an option, it might not be legal as the class may not have been written to allow inheritance)
  • You can write your own wrapper class that has the same interface + your new methods and delegate to the one you want to extend.

I prefer the delegation approach.


C# class extension methods are mostly syntactic sugar. You get the same functionality with free functions (i.e., functions with a reference or constant reference to your class as their first parameter). Since this works well for the STL, why not for your class?


In C++ you can use free functions, but sometimes extension methods work better when you nest many functions together. Take a look at this C# code:

var r = numbers.Where(x => x > 2).Select(x => x * x);

If we to write this in C++ using free function it would look like this:

auto r = select(where(numbers, [](int x) { return x > 2; }), [](int x) { return x * x; });

Not only is this difficult to read, but it is difficult to write. The common way to solve this is to create what is called a pipable function. These functions are created by overloading the | pipe operator(which is just really the or operator). So the code above could be written like this:

auto r = numbers | where([](int x) { return x > 2; }) | select([](int x) { return x * x; });

Which is much easier to read and write. Many libraries use pipable function for ranges, but it could be expanded to other classes as well. Boost uses it in their range library, pstade oven uses it, and also this C++ linq library uses it as well.

If you would like to write your own pipable function, boost explain how to do that here. Other libraries, however, provide function adaptors to make it easier. Pstade egg has a pipable adaptor, and linq provides the range_extension adaptor to create a pipable function for ranges as least.

Using linq, you first just create your function as a function object like this:

struct contains_t
{
    template<class Range, class T>
    bool operator()(Range && r, T && x) const
    { return (r | linq::find(x)) != boost::end(r); };
};

Then you initialize the function using static initialization like this:

range_extension<contains_t> contains = {};

Then you can use your pipable function like this:

if (numbers | contains(5)) printf("We have a 5");


Generally not. However, if the library does not create instances of the class that require your extension and you are able to modify all places in the app that create an instance of the class and require your extensions, there is a way you can go:

  • Create a factory function that is called at all places that require an instance of the class and returns a pointer to the instance (google for Design Patterns Factory, ...).
  • Create a derived class with the extensions you want.
  • Make the factory function return your derived class instead of the original class.

Example:


    class derivedClass: public originalClass { /* ... */};

    originalClass* createOriginalClassInstance()
    {
         return new derivedClass();
    }
  • Whenever you need to access the extensions, you need to cast the original cast to the derived class, of course.

This is roughly how to implement the "inherit" method suggested by Glen. Glen's "wrapper class with same interface" method is also very nice from a theoretical point of view, but has slightly different properties that makes it less probable to work in your case.


There is one way in which it can be done. And that's by relaxing your requirements a bit. In C++, people often say that the interface of a class consists not just of its member functions, but of all functions that work on the class.

That is, non-member functions which can be given the class as a parameter should be considered part of its interface.

For example, std::find() or std::sort() are part of the interface of std::vector, even though they aren't members of the class.

And if you accept this definition, then you can always extend a class simply by adding nonmember functions.


You cannot add methods or data physically to the class file which is in binary form. However, you can add methods and data (functionality and state) to the objects of that class by writing extension classes. This is not straight forward and requires Meta-Object-Protocol and Interface based programming. You need to do a lot to achieve this in C++ since it does not support Reflection out of the box. In such an implementation when you query for the interface implemented by your new extension class via the original class object pointer, the meta object implementation returns that interface pointer via the meta class object for the extension class that it creates at runtime. This is how many customizable (plugin based) software application frameworks work. However, you must remember that it requires many other MOP mechanisms to be written to instanciate meta objects for all the classes using dictionaries in which the object relations are described and give the correct interface pointers for the original and extended class objects. Dassault Systemes' CATIA V5 is written in such an architecture called CAA V5 where you can extend existing components by writing new extension classes with the desired functionality.


Sure you can:


template <typename Ext>
class Class: public Ext { /* ... */ };

That doesn't mean it's the best approach though.


Sorry, no. Once your code is in obj, you can not change it. If this can be done in VC partial classes would be supported already. There is one exception though, operator methods can be extended using global functions, pretty like how cout<< is implemented in STL.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号