44

Is it possible in C++ to extend a class(add a method) in a different source file without editing the original source file where the class is written?

In obj-c it is possible by writing another @interface AbcClass (ExtCategory) ... @end

I got compile-time error(s) when I tried something like this:

//Abc.h
class Abc {            //This class is from a 3rd party library....
                       //  ...I don't want to edit its source file.
    void methodOne();
    void methodTwo();

}


//Abc+Ext.h
class Abc {       // ERROR: Redefinition of 'Abc'
    void methodAdded();
}

My target is to retain the 'Abc' name and add methods to it. A specific class in a 3rd party library that I used lacks some methods and I want to add those methods but I am keeping the source file unedited.

Is there a way to do this? I am new in writing C++ codes. I am familiar with some of its syntax but don't know much.

acegs
  • 2,621
  • 1
  • 22
  • 31
  • 2
    To name another language, **C#** makes it possible to split the definition of a class over more than one file via a mechanism known as a [partial class](http://msdn.microsoft.com/en-us/library/vstudio/wa80x488.aspx). – DavidRR Sep 14 '13 at 18:13

5 Answers5

41

No. This kind of class extension is not possible in C++. But you can inherit a class from the original source file and add new functions in your source file.

//Abc.h
class Abc {
    void methodOne();
    void methodTwo();
};


//Abc+Ext.h
class AbcExt : public Abc {       
    void methodAdded();
};

You can then call methods as following:

std::unique_ptr<AbcExt> obj = std::make_unique<AbcExt>();
obj->methodOne(); // by the virtue of base class
obj->methodAdded(); // by the virtue of derived class
Community
  • 1
  • 1
Manoj Awasthi
  • 3,460
  • 2
  • 22
  • 26
13

There's a way to actually do this, but it requires the compiler to support #include_next. GCC has this, no idea about other compilers. It also needs to support at least C++11.

I wouldn't exactly call this trick beautiful, but it does the job.

Ensure your include path has the the directory where the "extension" file resides before the directory where the original code resides (i.e. if the original Abc.hpp is in src, then move it to src/some_dir). So in this case your include dirs would be -Isrc -Isrc/some_dir.

Your "extension" code should be in a file with the exact same name as the original code. So for this example that's Abc.hpp.

Here's the extension file's content:

#ifndef ABC_EXT_HPP_
#define ABC_EXT_HPP_

#include <utility>

namespace evil {
  // Search the include path for the original file.
  #include_next "Abc.hpp"
}

class Abc : public evil::Abc {
  public:
    /*
    // Inherit all constructors from base class. Requires GCC >=4.8.
    using evil::Abc::Abc;
    */

    /* Use the solution below if your compiler supports C++11, but not 
     * inheriting constructors.
     */
    template <class... Args>
    Abc (Args... args) : evil::ABC(std::forward<Args...>(args...)) { }

    ~Abc () { }

    void methodAdded () { /* Do some magic. */ }
};

#endif // ABC_EXT_HPP_

There's things missing in the example such as the assignment operator not being "forwarded" to the base class. You can use the same trick as used for the constructor to do that. There might be other things missing, but this should give you a starting point which works well enough for "simple" classes.

One thing I dislike is the creation of the "evil" namespace. However, anonymous namespaces can't help out here, because a new anonymous namespace will be created in each translation unit that includes Abc.hpp. That will lead to issues if your base class has e.g. static members.

Edit: Nevermind, the assignment operator (i.e. Abc bla = evil::Abc(9)) also works, because evil:Abc can be implicitly converted to Abc because that constructor exists.

Edit 2: You might run into a lot of trouble once there's nested namespaces involved. This happens as soon as there's an #include in the original Abc.hpp, because it will now be nested inside the evil namespace. If you know all of the includes, you could include them before declaring the evil namespace. Things get real ugly, real quick though.

AVH
  • 11,349
  • 4
  • 34
  • 43
2

There's no specific mechanism for doing this directly in the current C++, but there are several ways you can achieve something like it at the cost of some boiler-plate work:

Method 1:

// foo.h
class Foo {
private:      // stuff
public:       // stuff

private:
    // All this crap is private. Pretend like I didn't expose it.
    // yeah, I know, you have to compile it, and it probably adds
    // dependencies you don't want to #include, like <string>
    // or boost, but suck it up, cupcake. Stroustrup hates life.
    void internalHelper(std::string&, std::vector&, boost::everything&);
};

Method 2:

// foo.h
class Foo {
private:      // stuff
public:       // stuff
};

// fooimpl.h
// Internal file, do not export with the API.
class FooImpl : public Foo {
private:      // stuff
public:       // stuff
    // So yeah, you have to go thru a cast and an extra include
    // if you want to access this. Suck it up, cupcake.
    void internalHelper(std::string&, std::vector&, boost::everything&);
};

Method 3:

// foo.h
class Foo {
private:      // stuff
public:       // stuff

    // For the private api: this is the worst approach, since it
    // exposes stuff and forces include/cruft on consumers.
    friend void foo_internalHelper(std::string&, std::vector&, boost::everything&);
};

// foo.cpp

// don't make it static or anyone can make their own as a way to
// back door into our class.
void foo_internalHelper(...);

Method 4:

// foo.h
class Foo {
private:      // stuff
public:       // stuff

    // No dependencies, but nothing stops an end-user from creating
    // a FooPrivate themselves...
    friend class FooPrivate;
};

// foo1.cpp
class FooPrivate {
public:
    void fooInternalHelper(Foo* f) {
       f->m_privateInternalYouCantSeeMe = "Oh, but I can";
    }
};
kfsone
  • 23,617
  • 2
  • 42
  • 74
  • This is too bad - I can't resist -1 –  Sep 14 '13 at 18:17
  • 1
    @DieterLücking Which part? – kfsone Sep 14 '13 at 18:24
  • 1
    The general hiccup !? –  Sep 14 '13 at 18:26
  • (I removed the stuff about my proposal, I think it's probably inappropriate for an SO answer) – kfsone Sep 14 '13 at 18:27
  • @DieterLücking Changed it to "internalHelper". – kfsone Sep 14 '13 at 18:28
  • 2
    @kfsone: Thanks for your answer. My goal is to not edit the original source file where the class is written and I can still use the original class when calling the added method. I want to share my code to other people without the hassle of updating the original source file and creating an instance of a derived class with different name just to call the added method. In Obj-C this is possible. I hope in C++ too. – acegs Sep 14 '13 at 19:17
  • @DieterLücking I don't see why - "internalHelper" is an apt description of what people often do this for. E.g. if you put your implementation in one .cpp file and you want a standard log-line format (`void Foo::logHelper(const char* msg) { printf("[%s][%s:%u] c:%u a:%u %s\n", getTime(), user(), port(), connection(), accountID(), msg);`) – kfsone Sep 14 '13 at 20:07
2

You cannot extend the class Abc, period!

The only way out are freestanding functions like

Abc add(const Abc& a, int b);
Stack Danny
  • 7,754
  • 2
  • 26
  • 55
  • 1
    @xuwicha Not really: one common is Abc operator + (const Abc&, const T&); –  Sep 14 '13 at 19:11
  • It's hard to format a multi-line code in comments so I just make it simpler and inline. Based on your example code, can I call the add() like this: `abcOne.add(5)`? or this: `add(abcOne, 5)`? – acegs Sep 14 '13 at 19:41
  • 3
    @xuwicha it would be the latter unfortunately – Nicu Stiurca Sep 14 '13 at 20:40
  • you can add operator, e.g. % to somebody else class . `abcOne % 5` see "non-member operator" for details. – x4444 Jan 09 '21 at 01:04
2

i found out that c++ is better at doing this than obj-c.

i tried the following and it works great!

the key is, enclose all of your classes in a namespace and then extend your target classes there with the same class name.

//Abc.h
namespace LibraryA {
    class Abc {            //This class is from a 3rd party library....
                           //  ...I don't want to edit its source file.
        void methodOne();
        void methodTwo();

    }
}

//Abc+Ext.hpp
namespace MyProj {
    class Abc : public LibraryA::Abc {
        using Base = LibraryA::Abc;   //desc: this is to easily access the original class...
                                      //   ...by using code: Abc::Base::someOrigMethod();
        using Base::Base;             //desc: inherit all constructors.
        
    protected:
        //---added members:
        int memberAdded;
        
    public:
        //---added methods:
        void methodAdded();
        
        //---modified virtual member funcs from original class.
        void origMethod_A() override;
        
    }
}

//Abc+Ext.cpp
namespace MyProj {
    void Abc::origMethod_A() {
        //...some code here...
        Base::origMethod_A();    //desc: you can still call the orignal method
        //...some code here...
    }
}

//SomeSourceFile_ThatUses_Abc.cpp
namespace MyProj {      //IMPT NOTE: you really need to enclose your...
                        //   ...project specific code to a namespace so you can...
                        //   ...use the version of class Abc you extended.
                        
                        
    void SomeClass::SampleFunc(){
        Abc objX;                   //create MyProj::Abc object.
        objX.methodAdded();         //calls MyProj::Abc::methodAdded();
        objX.origMethod_A();        //calls MyProj::Abc::origMethod_A();
        
        Abc::Base objY;             //create Library::Abc object.
        //objY.methodAdded();       //method not existing.
        objY.origMethod_A();        //calls Library::Abc::origMethod_A();
        
        //...some code here...
    }
    
}

//SomeModule.cpp
namespace OtherNamespace {
    void SomeOtherClass::SampleOtherFunc(){
        Abc objZ;                   //create Library::Abc object.
        //objZ.methodAdded();       //method not existing.
        objZ.origMethod_A();        //calls LibraryA::Abc::origMethod_A();
    }
    
}

you can even extend class Abc differently within other module namespaces.

//MyLib_ModuleA_Classes.hpp
namespace MyLib_ModuleA {
    class Abc : public LibraryA::Abc {
        //...add extensions here...
        void addedMethod_X();
        void origMethod_A() override;    //own overriden behavior specific to this ModuleA only.
    }
    
}

//MyLib_ModuleB_Classes.hpp
namespace MyLib_ModuleB {
    class Abc : public LibraryA::Abc {
        //...add extensions here...
        void addedMethod_Y();
        void origMethod_A() override;    //own overriden behavior specific to this ModuleB only.
    }
    
}

if in case class Abc is in global namespace, though i haven't tried it yet, i think you can just replace LibaryA::Abc to ::Abc.

sorry for the very late answer i've been doing this approach for around 4 years now and it's structure is very well useful. i tried this in c++14 but i think this is still doable in c++11. now i used c++17 and it compiles fine. i'm planning to convert to c++20 when the compilers i used already completed c++20 features.

acegs
  • 2,621
  • 1
  • 22
  • 31