简体   繁体   中英

Dependency injection of class with variadic template member function

Context: I have a third party logger class that has a variadic log function taking an arbitrary nr of parameters. In my code base I have several classes that need to log information. Instead of hardcoding this 3rd party logger class I want to be able to supply an instance through dependency injection. This way I can also supply for example a null object if I want to disable logging at run time.

Example code:

//third partycode
class Logger{
public:
    template<typename ...Ts>
    void Log(const char * format, Ts... params);
};

//own code
class NullLogger{
public:
    template<typename ...Ts>
    void Log(const char * format, Ts... params); // doesn't do actual logging
};

class SomeClass{
public:
    template<typename LOGGER>
    void SetLogger(std::shared_ptr<LOGGER> logger){ m_logger = logger;}

    void SomeFunction(){
        m_logger->Log("test {0},{1}", 42, 13.37);
    }
private:
    ??? m_logger;
};

Question: How can I make above code work?

My requirements:

  1. I do not want to make SomeClass a template class.
  2. I can not change the third party Logger(I can choose a different design for my own NullLogger)
  3. I want to be able to swap out the logger that is being used by SomeClass at run time.

You will not be able to inject any dependence that is not designed to be injectable. Maybe logger is, you will try to see how Log function is implemented. The Log function is template, so it can't be overridden now:

//third partycode
class Logger{
public:
    template<typename ...Ts>
    void Log(const char * format, Ts... params);
};

What you should to is to check how the function Log is implemented

    template<typename ...Ts>
    void Log(const char * format, Ts... params)
    {maybe it calls here any virtual_function(format, params...);}

If it calls any virtual function you can override it:

//own code
class NullLogger: Logger{
public:
    virtual_function(const char * format, Ts... params); // override
};

Maybe there is another way, for instance you could provide any specific stream objects to be used by Logger.
So if you succeed with above you have everything injectable, otherwise there is no choice:

class SomeClass{
public:
    void SetLogger(std::shared_ptr<Logger> logger){ m_logger = logger;}

    void SomeFunction(){
        m_logger->Log("test {0},{1}", 42, 13.37);
    }
private:
    std::shared_ptr<Logger> m_logger;
};

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