C ++ General way of providing `operator <<` for middleware types

We use middleware that generates types for us for various programming languages, including C ++. For the structures generated for C ++, I want to enter code that can be used for various data transformations, for example, output to std::ostream. Let them say that we have the following structure:

struct Foo {
    int a;
    double d;
};

Let's say I modify the middleware compiler to create the following template function:

template<typename Visitor>
void visit( Visitor &v, const Foo &data )
{
    v.visit( "a", data.a );
    v.visit( "d", data.d );
}

now I can use this code for different ways, and it should not affect anything if not used, for example make std::ostream::operator<<:

struct OstreamVisitor {
    OstreamVisitor( std::ostream &os ) : m_os( os ) {}

    void visit( const char *name, int i ) { m_os << name << "=" << i << std::endl; }
    void visit( const char *name, double d ) { m_os << name << "=" << d << std::endl; }

    std::ostream &m_os;
};

std::ostream &operator<<( std::ostream &out, const Foo &data )
{
    OstreamVisitor v( out );
    visit( v, data );
    return out;
}

, , std::ostream &operator<< ,

template<typename T>
std::ostream &operator<<( std::ostream &os, const T &t );

, , . ​​ ? - , , .

. , . , - :

// system header
template<typename T>
struct visitable_tag;

// generated header
namespace FooNamespace {
struct Foo { ... };

template<>
struct visitable_tag<Foo> {};
}

 // or maybe have tag in special namespace
 namespace visitable_tag_namespace {
 template<>
 struct visitable_tag<FooNamespace::Foo> {};
 }

. , , , , .

+6
3

clang lib . . ,

0

Here is what I implemented:

// header before generated types
namespace visitor_details {
template<typename T>
struct visitor_caller : std::false_type {};
}

// generated header
// types in various namespace
namespace foobar {

struct Foo { // this struct has generated helper
    int a = 1;
    double d = 2;
};

struct NotFoo { // this one does not, for testing
    int c;
};

}
// injected code generation
namespace visitor_details {
template<>
struct visitor_caller<foobar::Foo> : std::true_type {
    template<typename Visitor, typename T>
    static void visit( Visitor &v, T &&f )
    {
        v.visit( "a", f.a );
        v.visit( "d", f.d );
    }
};
}

// usage
struct OstreamVisitor {
    OstreamVisitor( std::ostream &os ) : m_os( os ) {}

    void visit( const char *name, int i ) { m_os << name << "=" << i << std::endl; }
    void visit( const char *name, double d ) { m_os << name << "=" << d << std::endl; }

    std::ostream &m_os;
};

template<typename T, typename EN = typename std::enable_if<visitor_details::visitor_caller<T>::value>::type >
std::ostream &operator<<( std::ostream &out, const T &data )
{
    OstreamVisitor v( out );
    visitor_details::visitor_caller<T>::visit( v, data );
    return out;
}

int main()
{
    foobar::Foo f;
    std::cout << f;
    foobar::NotFoo n;
    //std::cout << n;
}

now if i uncomment the last line in main, the error says:

error: no match for 'operator <<<(operand types:' std :: ostream {aka std :: basic_ostream} and 'foobar :: NotFoo)

it looks like this is what i need

living example

0
source

All Articles