6

Can i design my logging-function in a way, that it accepts concatenated strings of the following form using C++?

int i = 1;
customLoggFunction("My Integer i = " << i << ".");

.

customLoggFunction( [...] ){
    [...]
    std::cout << "Debug Message: " << myLoggMessage << std::endl << std::endl
}

Edit:

Using std::string as the attribute to the function works for the concatenated string, but then a passed non-concatenated string like customLoggFunction("example string") produces a compile-time error saying the function is not applicable for char[]. When i overload the function in the following way...

customLoggFunction(std::string message){...}
customLoggFunction(char message[]){...}

... the concatenated strings seize to work.

I uploaded the code: http://coliru.stacked-crooked.com/a/d64dc90add3e59ed

Rico Chr.
  • 131
  • 1
  • 9
  • 2
    Possible duplicate of [How to concatenate a std::string and an int?](https://stackoverflow.com/questions/191757/how-to-concatenate-a-stdstring-and-an-int) – Max Vollmer Jun 09 '18 at 17:37
  • Hoe about using `std::string`? Then you can do e.g. `customLoggFunction("My Integer i = " + std::to_string(i) + ".");` – Some programmer dude Jun 09 '18 at 17:37
  • it's not about the function, what you want is about strings and operators. – perreal Jun 09 '18 at 17:38
  • Your method just needs to accept a string. It's absolutely irrelevant to the function how the string it receives was constructed. See the dup for how to concatenate strings and ints in C++. – Max Vollmer Jun 09 '18 at 17:38
  • This works for the concatenated string, but then it doesn't work when i pass a non-concatenated string like customLoggFunction("example string"). I get a compile time error saying the function is not applicable for char[]. Then when i overload with char[] the first option doesn't work. – Rico Chr. Jun 09 '18 at 17:41
  • I don't think you can do *exactly* that with a function. A macro, yes. Otherwise you can create a *class object* that overloads `operator<<` but it's non-trivial. – Galik Jun 09 '18 at 17:42
  • 2
    *"Using std::string as the attribute to the function works for the concatenated string, but then a passed non-concatenated string like customLoggFunction("example string") produces a compile-time error"* Can't reproduce: http://coliru.stacked-crooked.com/a/d54abfc020c8d1b9 – HolyBlackCat Jun 09 '18 at 17:46
  • Have a look @HolyBlackCat : http://coliru.stacked-crooked.com/a/d64dc90add3e59ed – Rico Chr. Jun 09 '18 at 18:35
  • @RicoChr. The parameter has to be const: `const char message[]`. – HolyBlackCat Jun 09 '18 at 21:06
  • Holy .... Black Cat. C++ Noob mistake :D Thank you very much. This platform and it's people are awesome... – Rico Chr. Jun 09 '18 at 21:10
  • @HolyBlackCat it still doesn't work. What the heck is wrong with C++, this should be such a basic task.... – Rico Chr. Jun 10 '18 at 08:45
  • @RicoChr. Alright, post the last version of your code. – HolyBlackCat Jun 10 '18 at 08:56
  • http://coliru.stacked-crooked.com/a/87667287543ad02a – Rico Chr. Jun 10 '18 at 09:00

4 Answers4

7

It's impossible to do with the exact syntax you asked for unless you resort to macros.

But if you don't mind replacing << with ,, then you can do following:

#include <iostream>
#include <string>
#include <sstream>

void log_impl(const std::string &str)
{
    std::cout << str;
}

template <typename ...P> void log(const P &... params)
{
    std::stringstream stream;

    (stream << ... << params);
    // If you don't have C++17, use following instead of the above line:
    // using dummy_array = int[];
    // dummy_array{(void(stream << params), 0)..., 0};

    log_impl(stream.str());
}

int main()
{
    log("1", 2, '3'); // prints 123
}
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • i'll check this, looks nice. – Rico Chr. Jun 09 '18 at 17:47
  • I get an error: parameter packs not expanded with ‘...’: (stream << this->title << params); Did i miss something? – Rico Chr. Jun 09 '18 at 18:17
  • Are you compiling with C++17 @RicoChr. since it seems to use the parameter expansion in a [fold expression](http://en.cppreference.com/w/cpp/language/fold) if I'm not mistaken. – Carl Jun 09 '18 at 18:21
  • I'm using this: gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 Copyright (C) 2015 Free Software Foundation, Inc. You tell me :D Sorry... But i'd say: No – Rico Chr. Jun 09 '18 at 18:24
  • 1
    @RicoChr. I've added a pre-c++17 alternative. – HolyBlackCat Jun 09 '18 at 19:04
4

For trivial projects this is one of the few things I use a MACRO for. You can do something like this:

#define LOG(m) do{ std::cout << timestamp() << ": " << m << '\n'; }while(0)

// ...

LOG("error: [" << errno "] " << filename << " does not exist.");

Generally MACROS should be avoided but there is no other way to get precisely this with a standard function. So...

Note: The empty condition do{...}while(0) enables you to place the MACRO in places that a MACRO usually can't go if it contains multiple statements.

Galik
  • 47,303
  • 4
  • 80
  • 117
2

You could do it by defining a new operator<<. From vague memory, implementing functions with these three signatures will do the trick:

std::string operator<<(const char * a, const std::string & b);
std::string operator<<(const std::string & a, const char * b);
std::string operator<<(const std::string & a, const std::string & b);

Each of them has to concatenate its arguments and return a std::string.

Howeever, it feels wrong. Goes against the grain of C++. I suggest a more C++-ish solution, namely to make your logger into a class, and write operator<<() members for that class, so you can run

customLog << "My Integer i = " << i << "." << "\n";
arnt
  • 8,949
  • 5
  • 24
  • 32
2

One approach is a simple utility class that uses a standard stream in a templated member function:

class LogStream {
    public:
        template <class T> LogStream& operator << (const T& rhs) {
            stream << rhs;
            return *this;
        }

    private:
        std::stringstream stream;
};

The stream member doing all the work is then used in the destructor,

~LogStream() {
    std::cout << stream.str() << std::endl;
}

and you can create temporary objects for passing your arguments to be concatenated:

LogStream() << "anything with std::ostream operator: " << 1.2345 << ' ' << std::hex << 12;

Additional state (e.g. a log level) can be passed to the constructor, often accompagnied by convenience functions like LogStream debug() { return LogStream(...); }. When you reach a certain point of sophistication though, you might want to switch to a logging library of course.

lubgr
  • 37,368
  • 3
  • 66
  • 117