0

I am invoking the below function from different classes and files like as shown below. And the implementation is almost similar. But it is initializing in the respective class constructor.

Can I create a common function for the below code for all the classes and then when invoking, based on the class can I initialize and return the object?

Below is the function with almost similar implementation for all the classes. And I have this similar implementation in around 10 places.

Could someone please suggest me the better approach to create a common reusable function?

1.

shared_ptr<CTestOneImpl> COneImpl::createTestObject(const string& f_strSFID, short f_nID,
                                                        bool f_bIsVerified,
                                                        bool f_bIsProcessed)
{
shared_ptr<CTestOneImpl> l_pTestObj = nullptr;
l_pTestObj = make_shared<CTestOneImpl>(f_nID, f_strSFID,
                                                    f_bIsVerified, f_bIsProcessed,
                                                    this);
 return l_pTestObj;
}
shared_ptr<CTestTwoImpl> CTwoImpl :: createTestObject(string f_hStrSFID, long f_nID,
                                                          bool f_bIsVerified,
                                                          bool f_bIsProcessed)
{
shared_ptr<CTestTwoImpl> l_pTestObj = nullptr;
l_pTestObj = make_shared<CTestTwoImpl>(f_nID, f_hStrSFID, f_bIsVerified
                                                , f_bIsProcessed, this);
return l_pTestObj;
}
shared_ptr<CTestThreeImpl> CThreeImpl ::createTestObject(const string& f_strSFID,
                                                     const string& f_nID,
                                                     bool f_bIsVerified,
                                                     bool f_bIsProcessed)
{
shared_ptr<CTestThreeImpl> l_pTestObj = nullptr;
l_pTestObj = make_shared<CTestThreeImpl>(f_nID,
                                              f_strSFID,
                                              f_bIsVerified,
                                              f_bIsProcessed,
                                              this);
return l_pTestObj;
}

Updated code using templatized class based on the inputs:

.h file

#include <iostream>
#include <list>

template <typename RetType, typename Args1, typename Args2, typename Args3>
class CTestImpl
{
public:
    CTestImpl(std::string f_lCallIdentifier, bool f_bIsVerified,
         bool f_bIsProcessed);

private:
    std::shared_ptr<RetType>
    createTestObject(Args1&& f_strSFID, Args2&& f_bIsVerified, Args3&& f_bIsProcessed);

public:
    void handleEvents(const std::string& f_eCallEvent, const std::string& f_strSFID);
};

.Cpp file

#include "TestImpl.h"

template <typename RetType, typename Args1, typename Args2, typename Args3>
// error: C2976: 'CTestImpl': too few template arguments
std::shared_ptr<RetType> CTestImpl<RetType>::createTestObject(Args1&& f_strSFID, Args2&& f_bIsVerified, Args3&& f_bIsProcessed)
// error: C2244: 'CTestImpl::createTestObject': unable to match function definition to an existing declaration
{
    return std::make_shared<RetType>(std::forward<Args1>(f_strSFID),
                                              std::forward<Args1>(f_bIsVerified),
                                              std::forward<Args1>(f_bIsProcessed));
}

//error: C2955: 'CTestImpl': use of class template requires template argument list
void CTestImpl::handleEvents(const std::string& f_eCallEvent, const std::string& f_strSFID)
{
    // error: C2509: 'handleEvents': member function not declared in 'CTestImpl'
    shared_ptr<CTestImpl> l_hCallObj = nullptr;
    l_hCallObj = createTestObject(f_strSFID, true, false);
}
John Paul Coder
  • 313
  • 1
  • 12
  • BTW: What is the use case to call a function which did really nothing but forward all parameters to exact another function. If you replace createTestObject() to make_shared() nothing else changes. But if there is any useful thing inside your create func, you simply make it a template. That will safe source code, the generated assembly should not change which means you did not safe code size for your prog. – Klaus Nov 17 '20 at 17:05
  • You could generate C++ code using [GPP](https://logological.org/gpp) – Basile Starynkevitch Nov 18 '20 at 13:29

2 Answers2

1

Not sure to understand what do you exactly want... but... what about as follows?

template <typename RetType>
shared_ptr<RetType> BaseClass::createTestObject (std::string const & f_strSFID,
                                                 short f_nID,
                                                 bool f_bIsVerified,
                                                 bool f_bIsProcessed)
{ return std::make_shared<RetType>(f_nId, f_strSFID, f_bIsVerified, f_bIsProcessed, this); }

Or, maybe better, if you pass the argument to createTestObject() (this also) in the same order required by the RetType constructor

template <typename RetType, typename ... Args>
shared_ptr<RetType> BaseClass::createTestObject (Args && ...as)
{ return std::make_shared<RetType>(std::forward<Args>(as)...)); }

But, at this point, you can directly call std::make_shared().

max66
  • 65,235
  • 10
  • 71
  • 111
  • Thank you @max66 for your valuable inputs. Based on your inputs i created a templatized class. But I am getting some errors. I have updated the original post with the changes. Please help me to resolve the errors. – John Paul Coder Nov 18 '20 at 13:28
  • @JohnPaulCoder - Suggestion: never (**never**!!!) define template classes or functions in `.cpp` files. Always (**always**!!!) declare **and** define the template stuff in the header files. See [this question](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) for more references. – max66 Nov 18 '20 at 14:10
  • @JohnPaulCoder - Second suggestion: when you're "getting some errors", transcribe them in the question. – max66 Nov 18 '20 at 14:13
  • Thank you @max66 for your valuable inputs. – John Paul Coder Nov 19 '20 at 03:54
0

You don't provide quite enough information, so I'm going to make some assumptions.

Assumptions

  1. The constructors of all the classes have the same signature. All of your calls to make_shared pass the same variables so I assume this is the case.
  2. All of the classes have similar functionality. You state that the implementations are almost the same, so let's assume this is the case.
  3. You have some way to differentiate between the classes. This is the assumption that would be best helped by knowing more information about what you're doing (maybe post the headers of some of the classes). For the purposes of this example, let's assume you know, at runtime, that you will want to instantiate a 'TestOne', 'TestTwo', or 'TestThree' object. I'm sure people will explain that this isn't the best or most efficient idea, and they would be correct, but this is real life and there are deadlines to meet and these classes appear to be test classes anyway so this should be sufficient.

Solution Use polymorphism.

/**
 * This is an interface that all of your test classes will need to implement.
 */
class ITest {
public:
   ~virtual ITest() = default;
   virtual void CommonMethod(...) = 0;
};

class CTestOneImpl : public ITest {
   void CommonMethod(...) override;
};

Assuming you are setup like the above for all 3 (or however many) of your classes, you can now create a factory class that will make some people sad.

class TestObjectFactory {
   enum class TestType{
       TestOne,
       TestTwo, 
       TestThree
   };

   static std::shared_ptr<TestObject> MakeTestObject(TestType &t, ...) {
       switch(t) {
       case(TestOne): return std::make_shared<CTestOneImpl>(...);
       ....
       default: return nullptr;
       }
   }
};

So when you want a CTestOneImpl, you just call auto test_obj_ptr = TestObjectFactory::MakeTestObject(TestObjectFactory::TestType::TestOne, ...)

Hope this answers your question, if it doesn't please provide some more info.

Josh A.
  • 374
  • 3
  • 15
  • Hi @Josh A, Thank you for your inputs: 1. Constructors of the classes don't have the same signature. And also argument size is also different in some of the classes. 2. Similar functionality in all the classes. 3. You are correct, at runtime, will want to instantiate 'TestOne', 'TestTwo', or 'TestThree' objects. – John Paul Coder Nov 18 '20 at 13:36