Nesting JLBorges' vout function

Here is JLBorges' elegant vout function that randomizes the cout function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <cstdlib>
#include <ctime>

template < typename STREAM_TYPE, STREAM_TYPE& stm >
struct basic_vstream
{
    template< typename T >
    static STREAM_TYPE& print_any( const T& v ) { return stm << v ; }

    template< typename FIRST, typename... REST >
    static STREAM_TYPE& print_any( const FIRST& first, const REST&... rest )
    {
        const std::size_t n = sizeof...(rest) + 1 ;
        if( std::rand() % n == 0 ) return stm << first ;
        else return print_any(rest...) ;
    }

    template < typename... ARGS >
    const basic_vstream& operator() ( const ARGS&... args ) const
    {
        print_any(args...) ;
        return *this ;
    }

    // etc

    const basic_vstream& operator() ( STREAM_TYPE& (*manip)( STREAM_TYPE& ) ) const
    { stm << manip ; return *this ; }

    // other manipulators
};

using vstream = basic_vstream< std::ostream, std::cout > ;
const vstream vout ;

int main()
{
    std::srand( std::time(nullptr) ) ;
    const char space = ' ' ;

    for( int i=0 ; i < 10 ; ++i )
        vout( "Attention!", "Hear this!", "Alert!" ) (space) ("We must leave!", "We're out of money!") (std::endl) ;
    std::cin.get();
} 


Anyone have any idea how to nest the vout function in the following sense?

1
2
3
4
vout( "Attention!", "Hear this!", "Alert!" ) (space)
    ( "We must leave!", "We're out of money!", 
    ( "I hate", "We detest", "I loathe" ) (space)
    ( "you!", ("them", "these guys", "those people")('.', '!')) );


In this example, one of "Attention!", "Hear this!", "Alert!" is randomly chosen, then space is chosen, and then the nested part:
Either "We must leave!" is chosen, or "We're out of money!" is chosen, or
( "I hate", "We detest", "I loathe" ) (space) ( "you!", ("them", "these guys", "those people")('.', '!'))
is chosen.

I guess the line 'if( std::rand() % n == 0 ) return stm << first ;'
needs to be modified if 'first' is itself of type basic_vstream (e.g. calling itself recursively even though first is only one argument because first is itself of form args...).
Just to note:
vout( "Attention!", ( "I hate you", "We detest you", "I loathe you" ) ) (std::endl) ;
compiles (though the randomization with args... is not right, always displaying "I loathe you"), but
vout( "Attention!", ( "I hate you", "We detest you", "I loathe you" ) ('.') (std::endl) ;
does not compile.

Ideally the solution should allow any depth of nestings, and no nestings needs to work as a special case.
Last edited on
closed account (3hM2Nwbp)
Isn't basic_vstream required to have a user-defined constexpr default constructor to define a constexpr global instance of the class?
Luc Lieber wrote:
Isn't basic_vstream required to have a user-defined constexpr default constructor to define a constexpr global instance of the class?

No, see: http://en.cppreference.com/w/cpp/language/constexpr


prestokeys wrote:
Anyone have any idea how to nest the vout function in the following sense?

My initial thought was to change your code to look like:
1
2
3
4
        vout("Attention!", "Hear this!", "Alert!") (space)
            ("We must leave!", "We're out of money!",
            vout("I hate", "We detest", "I loathe") (space)
                ("you", "them", "these guys", "those people")('.', '!')) (std::endl);


And add an overload of the basic_vstream::print_any function which "prints" a basic_vstream by just returning the stm template parameter. That is problematic though. The second vout call must be evaluated prior to the call it occurs in, so the last part is inserted into the stream before the first:

static STREAM_TYPE& print_any(const basic_vstream&) { return stm; }

http://ideone.com/c0hwRn (Note: I changed the constexpr lines to make the code compilable in VC++.)
Hmmm.... I was thinking of an extra-deep recursive call for print_any in the case that FIRST is basic_vstream, but perhaps all we needed was to overload print_any with basic_vstream. Here is an example run with some deep nestings, though the it still needs some tweaking to get the desired output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <cstdlib>
#include <ctime>

template < typename STREAM_TYPE, STREAM_TYPE& stm >
struct basic_vstream
{
    template< typename T >
    static STREAM_TYPE& print_any( const T& v ) {return stm << v ;}

    static STREAM_TYPE& print_any(const basic_vstream&) { return stm; }

    template< typename FIRST, typename... REST >
    static STREAM_TYPE& print_any( const FIRST& first, const REST&... rest )
    {
        const std::size_t n = sizeof...(rest) + 1 ;
        if( std::rand() % n == 0 ) return stm << first ;
		return print_any(rest...) ;
    }

    template < typename... ARGS >
    const basic_vstream& operator() ( const ARGS&... args ) const
    {
        print_any(args...) ;
        return *this ;
    }

    // etc

    const basic_vstream& operator() ( STREAM_TYPE& (*manip)( STREAM_TYPE& ) ) const
    { stm << manip ; return *this ; }

    // other manipulators
};

using vstream = basic_vstream< std::ostream, std::cout > ;
const vstream vout ;

int main()
{
    std::srand( std::time(nullptr) ) ;
    const char space = ' ' ;

    for( int i=0 ; i < 10 ; ++i )
        vout( "Attention!", "Hear this!", "Alert!" ) (space)
    	( "We must leave!", "We're out of money!", 
	    vout ( "I hate", "We detest", "I loathe" )(space)( 
	        vout ("you!", 
	            vout("them", "these guys", "those people")('.', '!')
	         )
            )
        )(std::endl) ;
    std::cin.get();
} 


Output (yikes!):
1
2
3
4
5
6
7
8
9
10
these guys.I loathe Alert!
these guys!you!We detest Hear this! We're out of money!
them!We detest Hear this! We must leave!
those people.We detest Attention! We must leave!
those people.you!I loathe Hear this!
these guys!We detest Attention! We must leave!
those people.We detest Attention!
them.you!I loathe Alert! We must leave!
those people!you!I loathe Hear this! We're out of money!
them!We detest Attention! We're out of money! 

Either the code still needs fixing, or I'm nesting the vouts wrong. But at least it compiles finally.
->That is problematic though. The second vout call must be evaluated prior to the call it occurs in, so the last part is inserted into the stream before the first

That is indeed the problem that remains. Everything is backwards (or really?), but clearly writing the vout commands backwards is not an acceptable solution. Perhaps define an inverse_basic_vstream class that carries out what basic_vstream does but in reverse? The special case of no nestings should then be left intact then, right?
Last edited on
I apologize for posting while having nothing useful to add, but I just need to say that those are some wonderful non sequiturs.
The order of evaluation problem can be easily solved; store the result of the evaluation when the expression is evaluated, and make that result available later.

Old wine in a new bottle:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>

struct one_of
{
    void one_of_( const one_of& v  ) { selected = v.selected ; }

    template< typename T > void one_of_( const T& v  )
    { std::ostringstream stm ; stm << v ; selected = stm.str() ; }

    template< typename FIRST, typename... REST >
    void one_of_( const FIRST& first, const REST&... rest )
    {
        const std::size_t n = sizeof...(rest) + 1 ;
        if( std::rand() % n == 0 ) one_of_(first) ;
    	else one_of_(rest...) ;
    }

    template < typename... ARGS > explicit one_of( const ARGS&... args )
    { one_of_(args...) ; }

    std::string selected ;
};

template < typename OSTREAM_TYPE >
OSTREAM_TYPE& operator<< ( OSTREAM_TYPE& stm, const one_of& v ) { return stm << v.selected ; }

int main()
{
    std::srand( std::time(nullptr) ) ;

    for( int i=0 ; i < 10 ; ++i )
        std::cout << one_of( 1, 2, 3, one_of( 4, "five", one_of( 6, "seven", 8 ) ), 9 ) << ' '
            << one_of( "one", one_of(1,2,3) ) << ' ' << one_of( "six", one_of(7,8,9) ) << '\n' ;
}

http://coliru.stacked-crooked.com/a/a8511e5c8e079c6b


> Isn't basic_vstream required to have a user-defined constexpr default constructor
> to define a constexpr global instance of the class?

clang++ seems to believe that this is a requirement.
AFAIK, the IS does not impose such a requirement.

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression.


A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used to create an object of its class type or when it is explicitly defaulted after its first declaration. The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer and an empty compound-statement. If that user-written default constructor would be ill-formed, the program is ill-formed. If that user-written default constructor would satisfy the requirements of a constexpr constructor, the implicitly-defined default constructor is constexpr. (emphasis added)


A type is a literal type if it is:
a scalar type; or
a reference type; or

a class type that has all of the following properties:
-- it has a trivial destructor,
-- every constructor call and full-expression in the brace-or-equal-initializers for non-static data
members (if any) is a constant expression,
-- it is an aggregate type or has at least one constexpr constructor or constructor template
that is not a copy or move constructor, and
-- it has all non-static data members and base classes of literal types; or

an array of literal type.
Topic archived. No new replies allowed.