boost::format and custom printing a std containers
Asked Answered
K

4

10

I have a function in my namespace ns that helps me print STL containers. For example:

template <typename T>
std::ostream& operator<<(std::ostream& stream, const std::set<T>& set)
{
    stream << "{";
    bool first = true;
    for (const T& item : set)
    {
        if (!first)
            stream << ", ";
        else
            first = false;
        stream << item;
    }
    stream << "}";
    return stream;
}

This works great for printing with operator << directly:

std::set<std::string> x = { "1", "2", "3", "4" };
std::cout << x << std::endl;

However, using boost::format is impossible:

std::set<std::string> x = { "1", "2", "3", "4" };
boost::format("%1%") % x;

The problem is fairly obvious: Boost has no idea that I would like it to use my custom operator << to print types which have nothing to do with my namespace. Outside of adding a using declaration into boost/format/feed_args.hpp, is there a convenient way to make boost::format look for my operator <<?

Kimberlykimberlyn answered 30/5, 2012 at 0:14 Comment(2)
I strongly suggest you take a look at this question, since it basically answers your needs. I won't vote to close as a duplicate, though, since your actual question is different (about the operator<<).Erb
@Xeo: My actual code uses a very similar approach to printing any container. Anyway, the issue isn't how to print a container with operator <<, it is how to make that same overload work for things where Koenig does not do what I want.Kimberlykimberlyn
C
4

I think the most clean way is to provide a thin wrapper in your own namespace for each of the operators you want to override. For your case, it can be:

namespace ns
{
    namespace wrappers
    {
        template<class T>
        struct out
        {
            const std::set<T> &set;

            out(const std::set<T> &set) : set(set) {}

            friend std::ostream& operator<<(std::ostream& stream, const out &o)
            {
                stream << "{";
                bool first = true;
                for (const T& item : o.set)
                {
                    if (!first)
                        stream << ", ";
                    else
                        first = false;
                    stream << item;
                }
                stream << "}";
                return stream;
            }
        };
    }

    template<class T>
    wrappers::out<T> out(const std::set<T> &set)
    {
        return wrappers::out<T>(set);
    }
}

Then use it like this:

std::cout << boost::format("%1%") % ns::out(x);
Carvel answered 3/10, 2012 at 9:14 Comment(1)
This is very similar to the solution I actually used. I have posted my solution as well.Kimberlykimberlyn
K
5

The solution I actually went with is quite similar to Answeror's, but it works for anything:

namespace ns
{

template <typename T>
class FormatWrapper
{
public:
    explicit FormatWrapper(const T& x) :
            ref(x)
    { }

    friend std::ostream& operator<<(std::ostream& stream,
                                    const FormatWrapper<T>& self
                                   )
    {
        // The key is that operator<< is name lookup occurs inside of `ns`:
        return stream << self.ref;
    }
private:
    const T& ref;
};

template <typename T>
FormatWrapper<T> Formatable(const T& x)
{
    return FormatWrapper<T>(x);
}

}

So usage is:

boost::format("%1%") % Formatable(x);
Kimberlykimberlyn answered 3/10, 2012 at 13:57 Comment(0)
C
4

I think the most clean way is to provide a thin wrapper in your own namespace for each of the operators you want to override. For your case, it can be:

namespace ns
{
    namespace wrappers
    {
        template<class T>
        struct out
        {
            const std::set<T> &set;

            out(const std::set<T> &set) : set(set) {}

            friend std::ostream& operator<<(std::ostream& stream, const out &o)
            {
                stream << "{";
                bool first = true;
                for (const T& item : o.set)
                {
                    if (!first)
                        stream << ", ";
                    else
                        first = false;
                    stream << item;
                }
                stream << "}";
                return stream;
            }
        };
    }

    template<class T>
    wrappers::out<T> out(const std::set<T> &set)
    {
        return wrappers::out<T>(set);
    }
}

Then use it like this:

std::cout << boost::format("%1%") % ns::out(x);
Carvel answered 3/10, 2012 at 9:14 Comment(1)
This is very similar to the solution I actually used. I have posted my solution as well.Kimberlykimberlyn
W
1

You can try something like this:

namespace boost // or __gnu_cxx
{
    using np::operator<<;
}
#include <boost/format/feed_args.hpp>
Wilow answered 18/8, 2012 at 23:35 Comment(0)
B
0

The problem as already noted is because of ADL (argument dependent lookup - often attributed to Andrew Koenig, but I believe he shouldn't get all the blame).

Even in your local context it wouldn't work in a template function where you intend to use your operator<<.

One cheating trick is to put the operator<< you define into namespace std. That is verboten, but it might work in your case, but only if it is put before its usage and that might be the problem.

There might be further options, such as defining your own Set template. I experimented with

    template<typename T> using Set=std::set<T>;

but couldn't get a solution that worked without the

    using np::operator<<;

yuyoyuppe provided.

Bernal answered 23/8, 2012 at 15:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.