Inherited Static Factory Method Returning Child Class Type

Viewed 45

I have a Logging class that is inherited by LoggingString, LoggingInt, etc. which each have a writeLog function that takes in the corresponding type:

class Logging {
public:
    explicit Logging(const LoggingConfig& config);
...
}

class LoggingString : public Logging {
public:
    using Logging::Logging;

    writeLog(std::string str);
...
}

In the code where I use LoggingString, I want to be able to call a factory function like this:

std::unique_ptr<LoggingString> logger = LoggingString::CreateIfLoggingEnabled(LoggingConfig{...});
std::unique_ptr<LoggingInt> logger = LoggingInt::CreateIfLoggingEnabled(LoggingConfig{...});

Considering they're all using the same constructor derived from the base Logging class, is it possible to put this static Logging::CreateIfLoggingEnabled function in the base class and somehow have it inherited by the Logging derived classes, but returning a unique_ptr<LoggingDerivedClass> instead? Maybe something with templating like this:

class Logging {
public:
    explicit Logging(const LoggingConfig& config);

    template<typename T> static std::unique_ptr<T> CreateIfLoggingEnabled(const LoggingConfig& config) {
        ...
        return std::unique_ptr<T>{};
    }
...
}

std::unique_ptr<LoggingString> logger = LoggingString::CreateIfLoggingEnabled<LoggingString>(LoggingConfig{...});

Not sure if this would work, and besides it seems redundant to have to specify LoggingString twice. Any other options?

1 Answers

it seems redundant to have to specify LoggingString twice.

You wouldn't have to specify LoggingString twice (thrice if you count the logger variable declaration). You can replace LoggingString:: with Logging::, and use auto for the variable, eg:

class Logging {
public:
    explicit Logging(const LoggingConfig& config);

    template<typename T>
    static std::unique_ptr<T> CreateIfLoggingEnabled(const LoggingConfig& config) {
        ...
        return std::make_unique<T>(config);
    }
    ...
};
auto logger = Logging::CreateIfLoggingEnabled<LoggingString>(LoggingConfig{...});
auto logger = Logging::CreateIfLoggingEnabled<LoggingInt>(LoggingConfig{...});

Demo

Alternatively, you can use CRTP to pass the derived type into the base class, eg:

template<typename Derived>
class Logging {
public:
    explicit Logging(const LoggingConfig& config);

    static std::unique_ptr<Derived> CreateIfLoggingEnabled(const LoggingConfig& config) {
        ...
        return std::make_unique<Derived>(config);
    }
    ...
};

class LoggingString : public Logging<LoggingString> {
public:
    using Logging<LoggingString>::Logging;

    void writeLog(std::string str);
    ...
};

class LoggingInt : public Logging<LoggingInt> {
public:
    using Logging<LoggingInt>::Logging;

    void writeLog(int i);
    ...
};
auto logger = LoggingString::CreateIfLoggingEnabled(LoggingConfig{...});
auto logger = LoggingInt::CreateIfLoggingEnabled(LoggingConfig{...});

Demo

Related