2

I'd like make a log function with multiple parameters, and I also want to call the log function with my class.
Here is my code (compiler : Visual studio 2019 or x86-64 gcc9.2)

Question 1> I cannot understand log function. Is it possible to use fold expression like this ? (this function comes from spdlog library)

Question 2> How could I use the log function with Mystruct class ?
log(1, MyStruct(1, 1.1f, "hello world"s)); // compiler error

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

template<typename ... Args>
void log(int level, Args const& ... args)
{
    std::ostringstream stream;
    using List = int[];
    (void)List {
        0, ((void)(stream << args), 0) ...
    };
    std::cout << stream.str() << std::endl;
}

class MyStruct
{
public:
    int val1 = 0;
    float val2 = 0.f;
    std::string val3;

    MyStruct(int v1, float v2, std::string_view const& v3) : val1(v1), val2(v2), val3(v3) {};
    std::string to_string() const
    {
        std::stringstream stream;
        stream << "val1=" << val1 << ", val2=" << val2 << ",val3=" << val3;
        return stream.str();
    }
};

std::ostringstream& operator<< (std::ostringstream& stream, MyStruct&& val)
{
    auto str = val.to_string();
    std::operator <<(stream, str);
    return stream;
}

void work_good()
{
    using namespace std::string_literals;  
    log(1, 1.1f, "hello world"s);
}

void compile_error()
{
    using namespace std::string_literals;  
    log(1, MyStruct(1, 1.1f, "hello world"s));
}

int main()
{
    work_good();
}
Isaiah
  • 685
  • 2
  • 15
  • 28
Eric Kim
  • 55
  • 5

2 Answers2

3

Question 1> I cannot understand log function. Is it possible to use fold expression like this ?

To be precise, it's not using fold expression (since C++17). See pack expansion, which was supported from C++11, and yes it's valid usage as in log.

A pattern followed by an ellipsis, in which the name of at least one parameter pack appears at least once, is expanded into zero or more comma-separated instantiations of the pattern, where the name of the parameter pack is replaced by each of the elements from the pack, in order.

and

Question 2> How could I use log function with Mystruct class ?

You declared operator<< taking MyStruct as rvalue-reference, but in log arguments are passed to operator<< as lvalue, which can't be bound to rvalue-reference. You can change the operator<< to taking lvalue-referenct to const, which is applicable for both lvalues and rvalues. e.g.

std::ostringstream& operator<< (std::ostringstream& stream, const MyStruct& val)
{
    auto str = val.to_string();
    std::operator <<(stream, str);
    return stream;
}

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • @eerorika Could you please explain more? I tried OP's code which could compile in pre-C++17 mode. – songyuanyao Feb 19 '20 at 03:34
  • Really? I may be confused. – eerorika Feb 19 '20 at 03:35
  • @eerorika I think [this code](https://wandbox.org/permlink/09zIN9hgctmWk3wy) applies fold expression and [OP's code](https://wandbox.org/permlink/rYvLtvml5hsMrQMq) doesn't. Note the warning from Clang. – songyuanyao Feb 19 '20 at 03:38
  • I had not realised that that comma operator could be used even with the pack expansion. I'd only ever seen pack expansion with argument lists. Good to know. – eerorika Feb 19 '20 at 03:41
  • @songyuanyao Thanks, **you are right** I succeeded to build this code lol. Additionally I fixed a code more generically – Eric Kim Feb 19 '20 at 09:00
  • as @songyuanyao commented, log function is a [pack expansion](https://en.cppreference.com/w/cpp/language/parameter_pack#Pack_expansion)
    You can check expanded code [here](https://cppinsights.io/)
    – Eric Kim Feb 19 '20 at 09:38
0

I changed some code referred answer. I want to log function to be more generic

namespace nlog
{
    class MyStruct
    {
    public:
        int val1 = 0;
        float val2 = 0.f;
        std::string val3;

        MyStruct(int v1, float v2, std::string_view const& v3) : val1(v1), val2(v2), val3(v3) {};
        std::string to_string() const
        {
            std::stringstream stream;
            stream << "val1=" << val1 << ", val2=" << val2 << ",val3=" << val3;
            return stream.str();
        }
    };

    template < typename T, typename decltype(std::declval<T>().to_string())* = nullptr>
        std::ostream& operator<< (std::ostream& stream, T&& val)
    {
        auto str = val.to_string();
        std::operator <<(stream, str);
        return stream;
    }


    template <typename ... Ts>
    void tolog(Ts && ...args)
    {
        std::stringstream strbuf;
        (strbuf << ... << std::forward<Ts>(args));

        std::cout << strbuf.str() << std::endl;
    }

    template <typename ... Ts>
    void toout(Ts && ...args)
    {
        (std::cout << ... << args);
    }
};

namespace nlog
{
    void Test1()
    {
        using namespace std::string_literals;
        std::stringstream strbuf;
        strbuf << MyStruct(0, 0.1f, "Eric"s);
        std::cout << strbuf.str() << std::endl;
    }

    void Test2()
    {
        using namespace std::string_literals;
        tolog(1, 1.1f, "hello world"s);
    }

    void Test3()
    {
        using namespace std::string_literals;
        tolog("I like this func val=", 100, ", youvalue=", 1.0f, ", MyStruct=",  MyStruct(0, 0.1f, "Eric"s));
    }
};

int main()
{
    nlog::Test1();
    nlog::Test2();
    nlog::Test3();
}
Eric Kim
  • 55
  • 5