Search code examples
c++template-meta-programminggeneric-programming

Use templated type injected via a template


This is my first time trying to apply does concept which are undoubtedly hard to grasp. I have created a generic logger type which can, at compile time, decide if the log level is high enough for output. Here is an example of the problem on compiler explorer: https://godbolt.org/z/2u4HhB. This is the logger:

static const int debug = 1;
static const int info = 2;
static const int warn = 3;
static const int error = 4;
static const int fatal = 5;

template<int level, bool should_log = false>
struct logger_function {
    static void log(std::string message) {
        // Do nothing in the default case.
    }
};

template<int level>
struct logger_function<level, true> {
    static void log(std::string message) {
        std::cerr << "[" << level << "] : " << message << std::endl;
    }
};

template<int minLevel>
struct std_err_logger {
    template<int levelValue>
    struct level {
        static constexpr bool shouldLogResult = levelValue >= minLevel;

        typedef logger_function<levelValue, shouldLogResult> log_function;

        static void log(std::string message) {
            log_function::log(message);
        }
    };
};

and it is used like this: std_err_logger<debug>::level<info>::log("Message.");

So far it works great. The problem starts when I try to inject the logger type via another template - after all I may prefer logging to a file in the future.

 template<typename logger>
    class log_client {
        log_client() {
            logger::level<fatal>::log("Worked!");
        }
    };

And injecting the type to the client:

int main() {
    log_client<std_err_logger<debug>> log();
    return 0;
}

But the mighty g++ compiler is not in a happy mood:

src/alsa/alsa_frame_recorder.h: In constructor ‘alsa::log_client<logger>::log_client()’:
src/alsa/alsa_frame_recorder.h:21:31: error: ‘::log’ has not been declared
             logger::level<1>::log("Worked!");
                               ^~~
src/alsa/alsa_frame_recorder.h:21:31: note: suggested alternative: ‘long’
             logger::level<1>::log("Worked!");
                               ^~~
                               long

Solution

  • The problem is that you use a dependent template incorrectly. You should write:

    template<typename logger>
    class log_client {
        log_client() {
            logger::template level<fatal>::log("Worked!");
        }
    };
    

    For an explanation why you need template here please refer to this question.