My own functions

JLBorges' random_if_else_if has inspired me to write out my own functions and share them here. I'm sure none of these will be original ideas, but I could not find them (or did not try to find them), so I wrote them out myself. Perhaps others can benefit from these. But hopefully, someone can point out some improvements since I'm still learning the language.

This first one is about displaying variations of sentences, so you don't get bored of reading the same lines over and over in a program. Ever had that feeling? Well, I did (with my own programs). So instead of cout << "Sentence", I have here vout (for variational-out) {groups of sentences, one chosen randomly according to some weight factor}:

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
55
56
57
58
59
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <string>
#include <numeric>

template <typename S>
struct VariationalSentences {
	std::vector<S> sentences;
	VariationalSentences (const std::initializer_list<S>& il) {
		sentences = il;
	}
};

template <typename T>
struct ProbabilityWeightFactors {
	std::vector<T> weights;
	ProbabilityWeightFactors (const std::initializer_list<T>& il) {
		weights = il;
	}
};

template <typename S = std::string, typename T = int>
void vout (const std::initializer_list<S>& s, const std::initializer_list<T>& w = std::initializer_list<T>(), std::ostream& os = std::cout) {
	VariationalSentences<S> v (s);
	if (w.size() == 0)
		os << v.sentences[std::rand() % v.sentences.size()] << std::endl;
	else
	{
		const int N = std::accumulate (w.begin(), w.end(), 0), r = std::rand() % N, W = w.size();
		int weightsPartialSums[W];
		std::partial_sum (w.begin(), w.end(), weightsPartialSums);
		for (int i = 0; i < W; i++)
			if (r < weightsPartialSums[i])
			{
				os << v.sentences[i] << std::endl;
				break;
			}
	}
}

int main () {  // testing the randomness of vout with loops
	std::srand (std::time (nullptr));
	
	for (int i = 0; i < 100; i++)
		vout ({"Hello.", "Hi.", "Howdy.", "Bonjour.", "Konnichiwa."});  // Each sentence occurs roughly at the same frequency.
	std::cout << std::endl;
	
	for (int i = 0; i < 100; i++)
		vout<std::string, double> ({"Hello.", "Hi.", "Howdy.", "Bonjour.", "Konnichiwa."}, {2.1, 3.14, 5, 4, 1.005});  // Sentences occur according to these weight factors
	std::cout << std::endl;
		
	for (int i = 0; i < 100; i++)
		vout ({"Hello.", "Hi.", "Howdy.", "Bonjour.", "Konnichiwa."}, {1, 3});  // Displays "Hi" about 3 times as often as "Hello" (the others never appear).
		
	std::cin.get();
	return 0;
} 


Of course, this does not work when I try to mix different types of outputs (e.g. vout ({string, int, string, double}). Perhaps someone can show me how to improve my vout function so it can do that (my attempt with variadic templates failed miserably, but I think variadic templates is to way to do it, but the approach will have to change completely from the above).
Last edited on
> this does not work when I try to mix different types of outputs (e.g. vout ({string, int, string, double})

std::initializer_list<T> is a proxy for an array of objects of type T; that is: all objects must be of the same type.


> I think variadic templates is to way to do it

That is the most efficient way to do it; another way is with a discriminated union (for instance boost::any)


With variadic templates, it could look like something like this (using equal weights for brevity):

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
55
56
57
58
59
60
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <iterator>
#include <vector>

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 )
    {
        constexpr 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 > ;
using wvstream = basic_vstream< std::wostream, std::wcout > ;
constexpr vstream vout ;
constexpr wvstream wvout ;

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

    for( int i=0 ; i < 12 ; ++i )
    {
        vout( "For", "In", "Within" ) (space)
            ( "a few", "two", 2, 2.5, "three", 3, 4.2 )(space)
            ( "seconds", "minutes", "hours", "days" )  (',') (space)
            ( "some", "two", 3, 4, "few" ) (space)
            ( "cats", "dogs", "boys", "girls" ) (space)
            ( "will", "should", "would", "might") ( "", " not" ) (space)
            ( "jump", "leap", "walk", "talk" ) (space)
            ( "over", "under", "beside", "atop", "to" )  (space)
            ( 'a', "one", "some", 1, "every", "each" ) (space)
            ( "tree", "car", "chair", "house" ) ('.') (std::endl) ;
    }
}

http://coliru.stacked-crooked.com/a/fb7b9a5e31a8dde0
Ok, here is my generalization of JLBorges' generalization of my vout function. It handles all manipulators and it handles probability weight factors. But alas, parameter packs must always be the last function parameter, so the weight factors must appear first in the argument (and hence cannot have a default empty value), forcing me to overload print_any and operator().

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <initializer_list>
#include <list>
#include <numeric>

template < typename STREAM_TYPE, STREAM_TYPE& stm, typename I = int>
struct basic_vstream
{
    template< typename T >
    static STREAM_TYPE& print_any( const T& v ) { return stm << v ; }  // base case function

    template< typename FIRST, typename... REST >
    static STREAM_TYPE& print_any( const FIRST& first, const REST&... rest )  // recursive case function
    {
    	constexpr std::size_t n = sizeof...(rest) + 1 ;
        if( std::rand() % n == 0 ) 
			return print_any(first); 
        else 
			return print_any(rest...) ;  // recursive call, but if rest... is only a single parameter, then rest... becomes first with the new rest... being empty (in which case n = 1, and the if-condition will be true)
    }

    template < typename... ARGS >
    const basic_vstream& operator() ( const ARGS&... args ) const  // struct basic_vstream will now be turned into a function object (with any number of parameters of any type)
    {
        print_any(args...) ;
        return *this ;  // returning basic_vstream& is required to allow for concatenation (print_any itself can return void, but it is good policy to return the stream_type, like other STL functions do).
    }

	// Overloading of the above functions to handle the presence of probability weight factors (parameter packs must always be the last function parameter, so weights cannot have a default value). 
    template< typename T >
    static STREAM_TYPE& print_any(std::list<I>& weights, const T& v ) { return stm << v ; } 

    template< typename FIRST, typename... REST >
    static STREAM_TYPE& print_any(std::list<I>& weights, const FIRST& first, const REST&... rest )  // weights not const due to pop_front
    {
    	const int N = std::accumulate (weights.begin(), weights.end(), 0);
		if( std::rand() % N < weights.front() ) 
			return print_any(weights, first);
		else 
        {
        	weights.pop_front();
			return print_any(weights, rest...) ;
    	}
	}

    template < typename... ARGS >
    const basic_vstream& operator() ( const std::initializer_list<I>& weights, const ARGS&... args ) const 
    {
    	std::list<I> listWeights = weights;  // cannot be const
        print_any(listWeights, args...) ;
        return *this ;
    }

	STREAM_TYPE& StreamType() const {return stm;}

    const basic_vstream& operator() ( STREAM_TYPE& (*manip)( STREAM_TYPE& ) ) const  // e.g. for std::endl
    { 
		stm << manip ; 
		return *this ; 
	}

    const basic_vstream& operator() ( std::ios_base& (*manip)( std::ios_base& ) ) const  // for other manipulators
    { 
		stm << manip ; 
		return *this ; 
	}
	
    const basic_vstream& operator() ( std::ios& (*manip)( std::ios& ) ) const
    { 
		stm << manip ; 
		return *this ; 
	}
	
	template <typename CharT, typename Traits>
    const basic_vstream& operator() ( std::basic_ios<CharT, Traits>& (*manip)( std::basic_ios<CharT, Traits>& ) ) const
    { 
		stm << manip ; 
		return *this ; 
	}
	
    const basic_vstream& operator() ( std::streambuf* sb ) const
    { 
		stm << sb ; 
		return *this ; 
	}
};

using vstream = basic_vstream< std::ostream, std::cout, double > ;
using wvstream = basic_vstream< std::wostream, std::wcout > ;
constexpr vstream vout ;
constexpr wvstream wvout ;

int main()  // Test showing the correct randomness property of vout
{
    std::srand( std::time(nullptr) ) ;
    const char space = ' ' ;
    for( int i=0 ; i < 100 ; ++i )
    {
    	//vout.StreamType().width(22); // This works, but it uglifies the test.
        vout //(std::left, std::right, std::internal)
			({1, 5.1, 20}, "In", "Within", "After", "Sometime within", "Shortly after" ) (space) // the last two strings will never show up
            ( "a few", "two", 2, 2.5, "three", 3, 4.2 )(space)
            ( "seconds", "minutes", "hours", "days" )  (',') (space)
            ( "some", "two", 3, 4, "a few" ) (space)
            ( "dudes", "gals", "boys", "girls" ) (space)
            ( "will owe me", "must pay me" ) (space)
			(std::uppercase, std::nouppercase)(std::showbase, std::noshowbase)(std::dec, std::oct, std::hex) (std::fixed, std::scientific) 
			(5, "ten", 80.75) (" dollars", " cents") 
			({1, 3.14}, '.', '!') (std::endl);  // about 3.14 times as many '!' than '.' will show
    }
    std::cin.get();
    return 0;
} 
Last edited on
Ok, we're not done with this yet. What if we want to wordwrap our sentences to fit nicely on the page? What if each separate concatenated part depends on various circumstances, and yet we want all the parts put into one (wordwrapped) paragraph? Well, following JLBorges' recursive use of variadic templates I've written the following SentenceAppender<I> class to handle this (with weighted probabilities):
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <initializer_list>
#include <list>
#include <sstream>
#include <numeric>
#include <typeinfo>

template <typename T> 
const std::string toString (T t) {
    std::ostringstream os;
    os << t;
    return os.str();
}

template <typename I>
struct SentenceAppender {
	std::string sentence;
	
    template <typename T>
    SentenceAppender& initialize (const T& t) {  // base case function
		if (typeid (T) == typeid (std::string)) 
			sentence = t;
		else
			sentence = toString (t);
		return *this;
	}  

    template <typename FIRST, typename...REST>
    SentenceAppender& initialize (const FIRST& first, const REST&...rest) {  // recursive case function
    	const std::size_t N = sizeof...(rest) + 1;
        if (std::rand() % N == 0) 
			return initialize (first); 
        else 
			return initialize (rest...); 
    	return *this;
	}
	
    template <typename T>
    SentenceAppender& append (const T& t) {  
		if (typeid (T) == typeid (std::string)) 
			sentence += t;
		else
			sentence += toString (t);
		return *this;
	}  

    template <typename FIRST, typename...REST>
    SentenceAppender& append (const FIRST& first, const REST&...rest) {  
    	const std::size_t N = sizeof...(rest) + 1;
        if (std::rand() % N == 0) 
			return append (first); 
        else 
			return append (rest...);
    	return *this;
	}

    template <typename... ARGS>
    SentenceAppender& operator() (const ARGS&... args) {
        append (args...);
        return *this;  // returning SentenceAppender& is required to allow for concatenation
    }
    
	// overloading the above to allow for probability weights
    template <typename T>
    SentenceAppender& initialize (std::list<I>& weights, const T& t) { 
		if (typeid (T) == typeid (std::string)) 
			sentence = t;
		else
			sentence = toString (t);
		return *this;
	} 

    template <typename FIRST, typename...REST>
    SentenceAppender& initialize (std::list<I>& weights, const FIRST& first, const REST&...rest) {   // weights cannot be const
    	const int N = std::accumulate (weights.begin(), weights.end(), 0);
		if (std::rand() % N < weights.front()) 
			return initialize (weights, first); 
        else
		{	
			weights.pop_front(); 
			return initialize (weights, rest...); 
    	}
		return *this;
	}
	
    template <typename...ARGS>
    SentenceAppender& initialize (const std::initializer_list<I>& weights, const ARGS&... args) { 
    	std::list<I> listWeights = weights;  // cannot be const
    	initialize (listWeights, args...);
    	return *this;
    }
    
    template <typename T>
    SentenceAppender& append (std::list<I>& weights, const T& t) {  
		if (typeid (T) == typeid (std::string)) 
			sentence += t;
		else
			sentence += toString (t);
		return *this;
	}  

    template <typename FIRST, typename...REST>
    SentenceAppender& append (std::list<I>& weights, const FIRST& first, const REST&...rest) {  
    	const int N = std::accumulate (weights.begin(), weights.end(), 0);
		if (std::rand() % N < weights.front()) 
			return append (weights, first); 
        else
		{	
			weights.pop_front(); 
			return append (weights, rest...);
		}
    	return *this;
	}

    template <typename...ARGS>
    SentenceAppender& operator() (const std::initializer_list<I>& weights, const ARGS&... args) {
    	std::list<I> listWeights = weights;  // cannot be const
        append (listWeights, args...);
        return *this; 
    }
};

const char space = ' ' ;
const std::string INDENT = "     ";
const std::string NEW = "  ";

std::string wordWrap (std::string str, size_t width = 70) { 
	size_t currentWidth = width;
	const int newSentenceSpaceLength = NEW.length();
    while (currentWidth < str.length ()) {
        std::string::size_type spacePos = str.rfind (' ', currentWidth);
        if (spacePos == std::string::npos)
            spacePos = str.find (' ', currentWidth);
        if (spacePos != std::string::npos) 
		{
            str[spacePos] = '\n';
            for (int i = 0; i < newSentenceSpaceLength - 1; i++)
            {
            	if (str[spacePos + 1] == ' ') 
				{
            		str.erase (spacePos + 1, 1);
            		spacePos++;
            	}
			}
            currentWidth = spacePos + width + 1;
        }
    }
    return str;
}

struct Person {
	std::string name;
	int salary;
	Person (const std::string& thisName, int thisSalary): name (thisName), salary (thisSalary) {} 
};

SentenceAppender<int> sapp;

int main () {  // Showing how SentenceAppender<I> works
    std::srand (std::time (nullptr));
    Person mark ("Mark", 15000), sam ("Sam", 40000);
    
    sapp.initialize (INDENT)("Wow!", "Hey!", "My god!")(space)(mark.name)(", You earn a", ", You make a")(space);
    if (mark.salary < 20000)
    {
    	sapp ({1,2,1}, "measly $", "lousy $", "crummy $")(mark.salary)(space)("per year?", "per year???")(NEW);
    	if (2 * mark.salary < sam.salary)
    		sapp (sam.name)(", whom I thought was poor,", ", whom I've made fun of,")(space)("earns more than", "makes more than")(space)('$')
				(2 * mark.salary)(", which is double your salary.", ", i.e. twice your salary.")(NEW)("Are you sure about your career?", "Ever consider getting a new career?");
		else
			sapp ("Well, at least you have your health.", "But hey, life goes on, right?");
    }
    else
    	sapp ("whopping", "cool")(space)(mark.salary)(space)("per year!", "per year!!!")(NEW)("That is so awesome!");
    std::cout << wordWrap (sapp.sentence) << std::endl;

    sapp.initialize ({3,1}, INDENT, NEW)({20,3}, "Now hear this...", "Now I have this to say...");
    std::cout << sapp.sentence << std::endl;
    
    std::cin.get();
    return 0;
} 


A typical output would be:
1
2
3
4
     Wow! Mark, You make a crummy $15000 per year??? Sam, whom I
thought was poor, earns more than $30000, which is double your salary.
Ever consider getting a new career?
     Now hear this...


Because everything is converted to string, this does not handle manipulators though.
Last edited on
> following JLBorges' recursive use of variadic templates

Not 'JLBorges' recursive use'; compile-time recursion is the the canonical way in which every programmer unpacks variadic template parameters one by one.


> What if we want to wordwrap our sentences to fit nicely on the page?
> Because everything is converted to string, this does not handle manipulators though.

Keep separate concerns separate? vout to a string stream, and then format the string as required for printing?

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <iterator>
#include <sstream>

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

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

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

std::stringstream stm ;
const basic_vstream< std::stringstream, stm > svout ;

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

    for( int i=0 ; i < 12 ; ++i )
    {
       svout( "For", "In", "Within" ) (space)
            ( "a few", "two", 2, 2.5, "three", 3, 4.2 )(space)
            ( "seconds", "minutes", "hours", "days" )  (',') (space)
            ( "some", "two", 3, 4, "few" ) (space)
            ( "cats", "dogs", "boys", "girls" ) (space)
            ( "will", "should", "would", "might") ( "", " not" ) (space)
            ( "jump", "leap", "walk", "talk" ) (space)
            ( "over", "under", "beside", "atop", "to" )  (space)
            ( 'a', "one", "some", 1, "every", "each" ) (space)
            ( "tree", "car", "chair", "house" ) ( ". " ) ;

        if( i%5 == 4 ) svout( "\n\n" ) ;
    }

    const int line_sz = 40 ;

    std::string line ;
    while( std::getline( stm, line ) )
    {
        if( line.empty() ) std::cout << '\n' ;
        else
        {
            for( std::size_t sz = 0 ; sz < line.size() ; sz += line_sz )
            {
                std::string s = line.substr( sz, line_sz ) ;
                if( s.size() == line_sz ) while( !std::isspace( s.back() ) )
                {
                    s.pop_back() ;
                    --sz ;
                }
                std::cout << s << '\n' ;
            }
        }
    }
}

http://coliru.stacked-crooked.com/a/e9df1cd7ce652cec
Why doesn't svout (std::endl) work? I thought all manipulators were already taken care of (at least for std::ostream)? What else needs to be added to basic_vstream to take care of svout (std::endl)? There is already:
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
    const basic_vstream& operator() ( STREAM_TYPE& (*manip)( STREAM_TYPE& ) ) const  
    { 
		stm << manip ; 
		return *this ; 
	}

    const basic_vstream& operator() ( std::ios_base& (*manip)( std::ios_base& ) ) const  
    { 
		stm << manip ; 
		return *this ; 
	}
	
    const basic_vstream& operator() ( std::ios& (*manip)( std::ios& ) ) const
    { 
		stm << manip ; 
		return *this ; 
	}
	
	template <typename CharT, typename Traits>
    const basic_vstream& operator() ( std::basic_ios<CharT, Traits>& (*manip)( std::basic_ios<CharT, Traits>& ) ) const
    { 
		stm << manip ; 
		return *this ; 
	}
	
    const basic_vstream& operator() ( std::streambuf* sb ) const
    { 
		stm << sb ; 
		return *this ; 
	}


I tested that
svout(std::left, std::right, std::internal);
svout(std::uppercase, std::nouppercase) (std::showbase, std::noshowbase) (std::dec, std::oct, std::hex) (std::fixed, std::scientific);
svout (std::boolalpha, std::noboolalpha);
all work, but not svout (std::endl). ???
Last edited on
> Why doesn't svout (std::endl) work?

std::endl is:
1
2
template< typename CHAR_TYPE, typename TRAITS_TYPE >
std::basic_ostream<CHAR_TYPE,TRAITS_TYPE>& std::endl( std::basic_ostream<CHAR_TYPE,TRAITS_TYPE>& );

http://en.cppreference.com/w/cpp/io/manip/endl

In const basic_vstream& operator() ( STREAM_TYPE& (*manip)( STREAM_TYPE& ) ) const
for svout, the STREAM_TYPE is std::stringstream.

For std::endl to work, replace
const basic_vstream& operator() ( STREAM_TYPE& (*manip)( STREAM_TYPE& ) ) const

with
1
2
3
4
5
6
7
using ostream_type = std::basic_ostream< typename STREAM_TYPE::char_type,
                                         typename STREAM_TYPE::traits_type > ;
const basic_vstream& operator() ( ostream_type& (*manip)( ostream_type& ) ) const
{
    stm << manip ;
    return *this ;
}
Topic archived. No new replies allowed.