Over-Engineered FizzBuzz, âÂÂ17-style
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
In my first question I tried to show âÂÂrealâ coding concerns while still keeping the implementation brute simple; using a loop over data rather than a string of separate testing statements, isolating the algorithm from the I/O of game play, allowing easy (compile-time) modification and extension.
Now, I present ðÂÂÂame 2.
A stateless algorithm has to do modulo (division) for every code word on every number, and division is a very fat and slow operation in the CPU. By remembering the state as an instance, generating the next number can be done by subtraction, keeping track of the remainder.
As before, itâÂÂs also an exercise to help me adopt âÂÂ17â features and review my style and habits to update them if needed to reflect the changing language, library, and culture.
configuration data input
Game 1 was hard coded to use the global fbchart
for its configuration. Game 2 takes a constructor argument, and it is flexible in being able to take not only any kind of âÂÂrangeâ of items, but allows for conformal assignment of the item itself. In particular, note that the internal state uses itemdef_t
which holds a std::string
, and the fbchart
uses const char*
. But, I can construct the player with the (primitive) array of pairs containing lexical string constants. Any container, and flexibility of whatâÂÂs in the container.
Let me go into more detail about the contraints and parameter checking here:
In the class, the constructor is declared
template<typename R ,
typename = std::enable_if_t<D3::is_range_t<R>::value> >
Fizzer_t (const R& range_of_itemdefs);
which takes anything that is a âÂÂrangeâ of some kind. If you call with completely unrelated parameters, like an int, this is not chosen (there is no overloading here, but that is the thought in general).
Then, inside the definition,
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
Once this function is chosen by overload resolution, then it checks that the contents of the range are conformal. So, if I mess up the definition of the structure I pass, rather than just getting âÂÂctor not foundâ (due to SFINAE), I get an error more suitable to the real problem.
(Aside: the static_assert
is after the place where the real usage error would occur, both lexically and in program-flow. There seems to be no standard as to the order of error messages anyway. On Visual Studio, I indeed get the static assert message first (only) rather than at the end of a template spew.)
results output
Imagine implementing a game for real. It probably wonâÂÂt use console output, but will draw icons and format fancy fonts and whatnot. So I donâÂÂt want a string output â I want the results in a usable form for handling in another function.
The instance provides a vector
of indexes into the configuration array, showing which of them matched. The config data can also be read from the object.
As indicated in Free your functions, it would be good if the rendering of the output was not a member function. Instead, I make sure that a non-member can be written, and the consumer of the library can do whatever he wants with it.
Supplied is a simple text string output routine, to make it complete and to illustrate how to write such a renderer for your fancy GUI screen.
I really donâÂÂt like writing fresh loose code to concatenate strings together with a delimiter in between them (not before the first or after the last), so I use boost::join
. But the strings are not existing in a neat list to feed it. Rather than make a temporary vector first, I use range adaptors (from Boost â Range.v3 is not quite ready for production work or Microsoft compilers) but the resulting specification playdef[i].second;
gets bloated into the lambda
[&playdef = self.get_playdefs()](size_t i) return playdef[i].second;
which I donâÂÂt find as clear and succinct as it should be. You still canâÂÂt beat normal code inside a for
loop â maybe I need a joiner that can be declared, fed in a loop, and then have the result extracted.
more iteration woes
The next
member function needs to iterate over two vectors in step. BoostâÂÂs combine
function crashes and burns big time when used with native C++17 features.
for (auto&[def,residue] : boost::combine(playdef, residues)) {
Though the docs give an example of this using BOOST_FOREACH
and then boost::tie
.
Because I want to play around with this some more, I put the guts of the loop into a separate function, even though it is small enough I would normally not have. I want to be able to comment-out the loop and put in a different one, easily.
top of source file
#include <utility>
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
#include <boost/range/irange.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include "d3/twostep.h"
#include "d3/is_range.h"
namespace D3 = Dlugosz::d3;
using namespace D3::twostep;
using std::string;
constexpr std::pair<int, const char*> fbchart
3, "Fizz" , 5, "Buzz" , // standard FizzBuzz
7, "Boom" // easily generalized and extended by adding to this chart!
;
game class
class Fizzer_t
public:
using itemdef_t = std::pair<int, std::string>;
private:
std::vector<itemdef_t> playdef;
std::vector<int> residues; // like remainder, but counts down.
std::vector<size_t> hits;
int val = 0; // current value
void dec_residue (size_t i, const itemdef_t& def, int& residue);
public:
template<typename R ,
typename = std::enable_if_t<D3::is_range_t<R>::value> >
Fizzer_t (const R& range_of_itemdefs);
int next(); // returns new value
auto const get_playdefs() const return playdef;
int get_val() const return val;
const auto& get_hits() const return hits;
;
template<typename R, typename>
Fizzer_t::Fizzer_t (const R& range_of_itemdefs)
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
residues.resize (playdef.size());
void Fizzer_t::dec_residue (size_t i, const itemdef_t& def, int& residue)
if (residue == 0)
// it was zero, so now reset to highest
residue= def.first - 1;
else
if (0 == --residue)
// if it hit zero now, record the match
hits.push_back (i);
int Fizzer_t::next()
++val;
hits.clear();
// I wish I could zip the two ranges and process as a range-for, so IâÂÂm isolating the real work
// from the looping construct here, to allow me to play around with the looping construct more.
// Range.v3 might work on current compiler??
for (auto i : boost::irange(size_t(), playdef.size()))
dec_residue (i, playdef[i], residues[i]);
return val;
rendering output as a free function
string render (const Fizzer_t& self)
string retval;
const auto& hits= self.get_hits();
if (!hits.size()) retval= std::to_string (self.get_val());
else
using boost::algorithm::join;
using boost::adaptors::transformed;
retval= join (
hits
return retval;
trivial program to call it
using std::cout;
void play2 (int upto)
Fizzer_t player1 fbchart ;
using boost::irange;
for (auto n : irange(1, upto+1))
int val= player1.next();
assert (val == n); // internal counter should increment the same as mine
cout << val << ": " << render(player1) << 'n';
int main()
play2 (100);
c++ fizzbuzz c++17
add a comment |Â
up vote
1
down vote
favorite
In my first question I tried to show âÂÂrealâ coding concerns while still keeping the implementation brute simple; using a loop over data rather than a string of separate testing statements, isolating the algorithm from the I/O of game play, allowing easy (compile-time) modification and extension.
Now, I present ðÂÂÂame 2.
A stateless algorithm has to do modulo (division) for every code word on every number, and division is a very fat and slow operation in the CPU. By remembering the state as an instance, generating the next number can be done by subtraction, keeping track of the remainder.
As before, itâÂÂs also an exercise to help me adopt âÂÂ17â features and review my style and habits to update them if needed to reflect the changing language, library, and culture.
configuration data input
Game 1 was hard coded to use the global fbchart
for its configuration. Game 2 takes a constructor argument, and it is flexible in being able to take not only any kind of âÂÂrangeâ of items, but allows for conformal assignment of the item itself. In particular, note that the internal state uses itemdef_t
which holds a std::string
, and the fbchart
uses const char*
. But, I can construct the player with the (primitive) array of pairs containing lexical string constants. Any container, and flexibility of whatâÂÂs in the container.
Let me go into more detail about the contraints and parameter checking here:
In the class, the constructor is declared
template<typename R ,
typename = std::enable_if_t<D3::is_range_t<R>::value> >
Fizzer_t (const R& range_of_itemdefs);
which takes anything that is a âÂÂrangeâ of some kind. If you call with completely unrelated parameters, like an int, this is not chosen (there is no overloading here, but that is the thought in general).
Then, inside the definition,
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
Once this function is chosen by overload resolution, then it checks that the contents of the range are conformal. So, if I mess up the definition of the structure I pass, rather than just getting âÂÂctor not foundâ (due to SFINAE), I get an error more suitable to the real problem.
(Aside: the static_assert
is after the place where the real usage error would occur, both lexically and in program-flow. There seems to be no standard as to the order of error messages anyway. On Visual Studio, I indeed get the static assert message first (only) rather than at the end of a template spew.)
results output
Imagine implementing a game for real. It probably wonâÂÂt use console output, but will draw icons and format fancy fonts and whatnot. So I donâÂÂt want a string output â I want the results in a usable form for handling in another function.
The instance provides a vector
of indexes into the configuration array, showing which of them matched. The config data can also be read from the object.
As indicated in Free your functions, it would be good if the rendering of the output was not a member function. Instead, I make sure that a non-member can be written, and the consumer of the library can do whatever he wants with it.
Supplied is a simple text string output routine, to make it complete and to illustrate how to write such a renderer for your fancy GUI screen.
I really donâÂÂt like writing fresh loose code to concatenate strings together with a delimiter in between them (not before the first or after the last), so I use boost::join
. But the strings are not existing in a neat list to feed it. Rather than make a temporary vector first, I use range adaptors (from Boost â Range.v3 is not quite ready for production work or Microsoft compilers) but the resulting specification playdef[i].second;
gets bloated into the lambda
[&playdef = self.get_playdefs()](size_t i) return playdef[i].second;
which I donâÂÂt find as clear and succinct as it should be. You still canâÂÂt beat normal code inside a for
loop â maybe I need a joiner that can be declared, fed in a loop, and then have the result extracted.
more iteration woes
The next
member function needs to iterate over two vectors in step. BoostâÂÂs combine
function crashes and burns big time when used with native C++17 features.
for (auto&[def,residue] : boost::combine(playdef, residues)) {
Though the docs give an example of this using BOOST_FOREACH
and then boost::tie
.
Because I want to play around with this some more, I put the guts of the loop into a separate function, even though it is small enough I would normally not have. I want to be able to comment-out the loop and put in a different one, easily.
top of source file
#include <utility>
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
#include <boost/range/irange.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include "d3/twostep.h"
#include "d3/is_range.h"
namespace D3 = Dlugosz::d3;
using namespace D3::twostep;
using std::string;
constexpr std::pair<int, const char*> fbchart
3, "Fizz" , 5, "Buzz" , // standard FizzBuzz
7, "Boom" // easily generalized and extended by adding to this chart!
;
game class
class Fizzer_t
public:
using itemdef_t = std::pair<int, std::string>;
private:
std::vector<itemdef_t> playdef;
std::vector<int> residues; // like remainder, but counts down.
std::vector<size_t> hits;
int val = 0; // current value
void dec_residue (size_t i, const itemdef_t& def, int& residue);
public:
template<typename R ,
typename = std::enable_if_t<D3::is_range_t<R>::value> >
Fizzer_t (const R& range_of_itemdefs);
int next(); // returns new value
auto const get_playdefs() const return playdef;
int get_val() const return val;
const auto& get_hits() const return hits;
;
template<typename R, typename>
Fizzer_t::Fizzer_t (const R& range_of_itemdefs)
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
residues.resize (playdef.size());
void Fizzer_t::dec_residue (size_t i, const itemdef_t& def, int& residue)
if (residue == 0)
// it was zero, so now reset to highest
residue= def.first - 1;
else
if (0 == --residue)
// if it hit zero now, record the match
hits.push_back (i);
int Fizzer_t::next()
++val;
hits.clear();
// I wish I could zip the two ranges and process as a range-for, so IâÂÂm isolating the real work
// from the looping construct here, to allow me to play around with the looping construct more.
// Range.v3 might work on current compiler??
for (auto i : boost::irange(size_t(), playdef.size()))
dec_residue (i, playdef[i], residues[i]);
return val;
rendering output as a free function
string render (const Fizzer_t& self)
string retval;
const auto& hits= self.get_hits();
if (!hits.size()) retval= std::to_string (self.get_val());
else
using boost::algorithm::join;
using boost::adaptors::transformed;
retval= join (
hits
return retval;
trivial program to call it
using std::cout;
void play2 (int upto)
Fizzer_t player1 fbchart ;
using boost::irange;
for (auto n : irange(1, upto+1))
int val= player1.next();
assert (val == n); // internal counter should increment the same as mine
cout << val << ": " << render(player1) << 'n';
int main()
play2 (100);
c++ fizzbuzz c++17
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
In my first question I tried to show âÂÂrealâ coding concerns while still keeping the implementation brute simple; using a loop over data rather than a string of separate testing statements, isolating the algorithm from the I/O of game play, allowing easy (compile-time) modification and extension.
Now, I present ðÂÂÂame 2.
A stateless algorithm has to do modulo (division) for every code word on every number, and division is a very fat and slow operation in the CPU. By remembering the state as an instance, generating the next number can be done by subtraction, keeping track of the remainder.
As before, itâÂÂs also an exercise to help me adopt âÂÂ17â features and review my style and habits to update them if needed to reflect the changing language, library, and culture.
configuration data input
Game 1 was hard coded to use the global fbchart
for its configuration. Game 2 takes a constructor argument, and it is flexible in being able to take not only any kind of âÂÂrangeâ of items, but allows for conformal assignment of the item itself. In particular, note that the internal state uses itemdef_t
which holds a std::string
, and the fbchart
uses const char*
. But, I can construct the player with the (primitive) array of pairs containing lexical string constants. Any container, and flexibility of whatâÂÂs in the container.
Let me go into more detail about the contraints and parameter checking here:
In the class, the constructor is declared
template<typename R ,
typename = std::enable_if_t<D3::is_range_t<R>::value> >
Fizzer_t (const R& range_of_itemdefs);
which takes anything that is a âÂÂrangeâ of some kind. If you call with completely unrelated parameters, like an int, this is not chosen (there is no overloading here, but that is the thought in general).
Then, inside the definition,
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
Once this function is chosen by overload resolution, then it checks that the contents of the range are conformal. So, if I mess up the definition of the structure I pass, rather than just getting âÂÂctor not foundâ (due to SFINAE), I get an error more suitable to the real problem.
(Aside: the static_assert
is after the place where the real usage error would occur, both lexically and in program-flow. There seems to be no standard as to the order of error messages anyway. On Visual Studio, I indeed get the static assert message first (only) rather than at the end of a template spew.)
results output
Imagine implementing a game for real. It probably wonâÂÂt use console output, but will draw icons and format fancy fonts and whatnot. So I donâÂÂt want a string output â I want the results in a usable form for handling in another function.
The instance provides a vector
of indexes into the configuration array, showing which of them matched. The config data can also be read from the object.
As indicated in Free your functions, it would be good if the rendering of the output was not a member function. Instead, I make sure that a non-member can be written, and the consumer of the library can do whatever he wants with it.
Supplied is a simple text string output routine, to make it complete and to illustrate how to write such a renderer for your fancy GUI screen.
I really donâÂÂt like writing fresh loose code to concatenate strings together with a delimiter in between them (not before the first or after the last), so I use boost::join
. But the strings are not existing in a neat list to feed it. Rather than make a temporary vector first, I use range adaptors (from Boost â Range.v3 is not quite ready for production work or Microsoft compilers) but the resulting specification playdef[i].second;
gets bloated into the lambda
[&playdef = self.get_playdefs()](size_t i) return playdef[i].second;
which I donâÂÂt find as clear and succinct as it should be. You still canâÂÂt beat normal code inside a for
loop â maybe I need a joiner that can be declared, fed in a loop, and then have the result extracted.
more iteration woes
The next
member function needs to iterate over two vectors in step. BoostâÂÂs combine
function crashes and burns big time when used with native C++17 features.
for (auto&[def,residue] : boost::combine(playdef, residues)) {
Though the docs give an example of this using BOOST_FOREACH
and then boost::tie
.
Because I want to play around with this some more, I put the guts of the loop into a separate function, even though it is small enough I would normally not have. I want to be able to comment-out the loop and put in a different one, easily.
top of source file
#include <utility>
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
#include <boost/range/irange.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include "d3/twostep.h"
#include "d3/is_range.h"
namespace D3 = Dlugosz::d3;
using namespace D3::twostep;
using std::string;
constexpr std::pair<int, const char*> fbchart
3, "Fizz" , 5, "Buzz" , // standard FizzBuzz
7, "Boom" // easily generalized and extended by adding to this chart!
;
game class
class Fizzer_t
public:
using itemdef_t = std::pair<int, std::string>;
private:
std::vector<itemdef_t> playdef;
std::vector<int> residues; // like remainder, but counts down.
std::vector<size_t> hits;
int val = 0; // current value
void dec_residue (size_t i, const itemdef_t& def, int& residue);
public:
template<typename R ,
typename = std::enable_if_t<D3::is_range_t<R>::value> >
Fizzer_t (const R& range_of_itemdefs);
int next(); // returns new value
auto const get_playdefs() const return playdef;
int get_val() const return val;
const auto& get_hits() const return hits;
;
template<typename R, typename>
Fizzer_t::Fizzer_t (const R& range_of_itemdefs)
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
residues.resize (playdef.size());
void Fizzer_t::dec_residue (size_t i, const itemdef_t& def, int& residue)
if (residue == 0)
// it was zero, so now reset to highest
residue= def.first - 1;
else
if (0 == --residue)
// if it hit zero now, record the match
hits.push_back (i);
int Fizzer_t::next()
++val;
hits.clear();
// I wish I could zip the two ranges and process as a range-for, so IâÂÂm isolating the real work
// from the looping construct here, to allow me to play around with the looping construct more.
// Range.v3 might work on current compiler??
for (auto i : boost::irange(size_t(), playdef.size()))
dec_residue (i, playdef[i], residues[i]);
return val;
rendering output as a free function
string render (const Fizzer_t& self)
string retval;
const auto& hits= self.get_hits();
if (!hits.size()) retval= std::to_string (self.get_val());
else
using boost::algorithm::join;
using boost::adaptors::transformed;
retval= join (
hits
return retval;
trivial program to call it
using std::cout;
void play2 (int upto)
Fizzer_t player1 fbchart ;
using boost::irange;
for (auto n : irange(1, upto+1))
int val= player1.next();
assert (val == n); // internal counter should increment the same as mine
cout << val << ": " << render(player1) << 'n';
int main()
play2 (100);
c++ fizzbuzz c++17
In my first question I tried to show âÂÂrealâ coding concerns while still keeping the implementation brute simple; using a loop over data rather than a string of separate testing statements, isolating the algorithm from the I/O of game play, allowing easy (compile-time) modification and extension.
Now, I present ðÂÂÂame 2.
A stateless algorithm has to do modulo (division) for every code word on every number, and division is a very fat and slow operation in the CPU. By remembering the state as an instance, generating the next number can be done by subtraction, keeping track of the remainder.
As before, itâÂÂs also an exercise to help me adopt âÂÂ17â features and review my style and habits to update them if needed to reflect the changing language, library, and culture.
configuration data input
Game 1 was hard coded to use the global fbchart
for its configuration. Game 2 takes a constructor argument, and it is flexible in being able to take not only any kind of âÂÂrangeâ of items, but allows for conformal assignment of the item itself. In particular, note that the internal state uses itemdef_t
which holds a std::string
, and the fbchart
uses const char*
. But, I can construct the player with the (primitive) array of pairs containing lexical string constants. Any container, and flexibility of whatâÂÂs in the container.
Let me go into more detail about the contraints and parameter checking here:
In the class, the constructor is declared
template<typename R ,
typename = std::enable_if_t<D3::is_range_t<R>::value> >
Fizzer_t (const R& range_of_itemdefs);
which takes anything that is a âÂÂrangeâ of some kind. If you call with completely unrelated parameters, like an int, this is not chosen (there is no overloading here, but that is the thought in general).
Then, inside the definition,
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
Once this function is chosen by overload resolution, then it checks that the contents of the range are conformal. So, if I mess up the definition of the structure I pass, rather than just getting âÂÂctor not foundâ (due to SFINAE), I get an error more suitable to the real problem.
(Aside: the static_assert
is after the place where the real usage error would occur, both lexically and in program-flow. There seems to be no standard as to the order of error messages anyway. On Visual Studio, I indeed get the static assert message first (only) rather than at the end of a template spew.)
results output
Imagine implementing a game for real. It probably wonâÂÂt use console output, but will draw icons and format fancy fonts and whatnot. So I donâÂÂt want a string output â I want the results in a usable form for handling in another function.
The instance provides a vector
of indexes into the configuration array, showing which of them matched. The config data can also be read from the object.
As indicated in Free your functions, it would be good if the rendering of the output was not a member function. Instead, I make sure that a non-member can be written, and the consumer of the library can do whatever he wants with it.
Supplied is a simple text string output routine, to make it complete and to illustrate how to write such a renderer for your fancy GUI screen.
I really donâÂÂt like writing fresh loose code to concatenate strings together with a delimiter in between them (not before the first or after the last), so I use boost::join
. But the strings are not existing in a neat list to feed it. Rather than make a temporary vector first, I use range adaptors (from Boost â Range.v3 is not quite ready for production work or Microsoft compilers) but the resulting specification playdef[i].second;
gets bloated into the lambda
[&playdef = self.get_playdefs()](size_t i) return playdef[i].second;
which I donâÂÂt find as clear and succinct as it should be. You still canâÂÂt beat normal code inside a for
loop â maybe I need a joiner that can be declared, fed in a loop, and then have the result extracted.
more iteration woes
The next
member function needs to iterate over two vectors in step. BoostâÂÂs combine
function crashes and burns big time when used with native C++17 features.
for (auto&[def,residue] : boost::combine(playdef, residues)) {
Though the docs give an example of this using BOOST_FOREACH
and then boost::tie
.
Because I want to play around with this some more, I put the guts of the loop into a separate function, even though it is small enough I would normally not have. I want to be able to comment-out the loop and put in a different one, easily.
top of source file
#include <utility>
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
#include <boost/range/irange.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include "d3/twostep.h"
#include "d3/is_range.h"
namespace D3 = Dlugosz::d3;
using namespace D3::twostep;
using std::string;
constexpr std::pair<int, const char*> fbchart
3, "Fizz" , 5, "Buzz" , // standard FizzBuzz
7, "Boom" // easily generalized and extended by adding to this chart!
;
game class
class Fizzer_t
public:
using itemdef_t = std::pair<int, std::string>;
private:
std::vector<itemdef_t> playdef;
std::vector<int> residues; // like remainder, but counts down.
std::vector<size_t> hits;
int val = 0; // current value
void dec_residue (size_t i, const itemdef_t& def, int& residue);
public:
template<typename R ,
typename = std::enable_if_t<D3::is_range_t<R>::value> >
Fizzer_t (const R& range_of_itemdefs);
int next(); // returns new value
auto const get_playdefs() const return playdef;
int get_val() const return val;
const auto& get_hits() const return hits;
;
template<typename R, typename>
Fizzer_t::Fizzer_t (const R& range_of_itemdefs)
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
residues.resize (playdef.size());
void Fizzer_t::dec_residue (size_t i, const itemdef_t& def, int& residue)
if (residue == 0)
// it was zero, so now reset to highest
residue= def.first - 1;
else
if (0 == --residue)
// if it hit zero now, record the match
hits.push_back (i);
int Fizzer_t::next()
++val;
hits.clear();
// I wish I could zip the two ranges and process as a range-for, so IâÂÂm isolating the real work
// from the looping construct here, to allow me to play around with the looping construct more.
// Range.v3 might work on current compiler??
for (auto i : boost::irange(size_t(), playdef.size()))
dec_residue (i, playdef[i], residues[i]);
return val;
rendering output as a free function
string render (const Fizzer_t& self)
string retval;
const auto& hits= self.get_hits();
if (!hits.size()) retval= std::to_string (self.get_val());
else
using boost::algorithm::join;
using boost::adaptors::transformed;
retval= join (
hits
return retval;
trivial program to call it
using std::cout;
void play2 (int upto)
Fizzer_t player1 fbchart ;
using boost::irange;
for (auto n : irange(1, upto+1))
int val= player1.next();
assert (val == n); // internal counter should increment the same as mine
cout << val << ": " << render(player1) << 'n';
int main()
play2 (100);
c++ fizzbuzz c++17
edited May 1 at 13:06
asked Apr 30 at 19:28
JDÃ Âugosz
5,047731
5,047731
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
3
down vote
accepted
Well, you already know that your code is Too Complicated, so I'll skip that part and just call out a few random things. :)
It's weird that you write function calls as f (x)
instead of f(x)
.
It's also weird that you write x= y
instead of x = y
.
template<typename R, typename>
Fizzer_t::Fizzer_t (const R& range_of_itemdefs)
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
residues.resize (playdef.size());
I'm pretty sure that by the time you get down to the static_assert
, the compiler will already have complained about the initialization of playdef
; so the static_assert
isn't really doing anything here.
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
Your use of curly-braces for initialization of std::vector
(with things-that-aren't-vector-elements) is problematic. Consider:
char x = "hello";
std::vector<std::string> vs std::begin(x), std::end(x) ; // oops!
versus:
char x = "hello";
std::vector<std::string> vs( std::begin(x), std::end(x) ); // fortunately fails to compile
The rule of thumb here is "use curlies when you know the exact elements you want to store into the container; use parentheses when you want to call a constructor which is acting as a factory function."
Your render
function is a bit hard to read. I'd write the same code like this:
std::string render(const Fizzer_t& self)
const auto& hits = self.get_hits();
if (hits.empty())
return std::to_string(self.get_val());
else
auto& playdefs = self.get_playdefs();
return boost::algorithm::join(
hits
That reminds me â look at this method!
auto const get_playdefs() const return playdef;
Two things obviously wrong with it: One, it returns a const
-qualified object â that's always wrong, because it disables move semantics. Two, the name of the method is get_playdefs
, plural, but the thing it's actually getting is spelled playdef
, singular. Pick one name and stick to it!
The third thing wrong with this method is that it returns a potentially expensive vector by copy instead of by reference.
The fourth thing arguably wrong with it is that it returns the whole vector, but the only place you actually use this function, you want to fetch only a single element from the vector.
One last thing: It seems weird that you wrap up the mutable counter variable, self.get_val()
, into the same object instance as the immutable configuration (get_playdef()
and so on). It would make a lot more sense to me if you'd architected it something more like
std::string render(int value, const Config& config)
std::vector<std::string> hits;
for (auto&& rule : config.rules)
if (value % rule.divisor == 0)
hits.push_back(rule.word);
return hits.empty() ?
config.render_default(value) :
boost::algorithm::join(hits, " ");
so that the mutable loop counter (value
) was separated from the immutable configuration. Here's a worked example of what I mean.
I already looked into the static_assert: stackoverflow.com/questions/49829932/â¦
â JDà Âugosz
May 16 at 20:40
I use asymmetric spacing on assignment, which helps distinguish it visually from comparison and other operators. C++ does not have âÂÂmethodsâÂÂ.
â JDà Âugosz
May 16 at 20:43
"I already looked into the static_assert": Check out my Wandbox link, in both GCC and Clang, which shows your static_assert not stopping the spew of compiler errors about the member-initializer. wandbox.org/permlink/a4F4rMjJCfM8olnS
â Quuxplusone
May 16 at 21:35
value % rule.divisor == 0)
thatâÂÂs missing the point, and going back to post #1. Look at the 3rd paragraph of the question!
â JDà Âugosz
May 17 at 1:14
1
The residues is the state, that the object is there to hold. It simply refers to the (fixed) config so it knows what to do. Your proposed value type would be my existing class. The residues is tied to the specific factors being used, so it needs to be tied permanently to the configuration.
â JDà Âugosz
May 18 at 6:24
 |Â
show 1 more comment
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
Well, you already know that your code is Too Complicated, so I'll skip that part and just call out a few random things. :)
It's weird that you write function calls as f (x)
instead of f(x)
.
It's also weird that you write x= y
instead of x = y
.
template<typename R, typename>
Fizzer_t::Fizzer_t (const R& range_of_itemdefs)
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
residues.resize (playdef.size());
I'm pretty sure that by the time you get down to the static_assert
, the compiler will already have complained about the initialization of playdef
; so the static_assert
isn't really doing anything here.
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
Your use of curly-braces for initialization of std::vector
(with things-that-aren't-vector-elements) is problematic. Consider:
char x = "hello";
std::vector<std::string> vs std::begin(x), std::end(x) ; // oops!
versus:
char x = "hello";
std::vector<std::string> vs( std::begin(x), std::end(x) ); // fortunately fails to compile
The rule of thumb here is "use curlies when you know the exact elements you want to store into the container; use parentheses when you want to call a constructor which is acting as a factory function."
Your render
function is a bit hard to read. I'd write the same code like this:
std::string render(const Fizzer_t& self)
const auto& hits = self.get_hits();
if (hits.empty())
return std::to_string(self.get_val());
else
auto& playdefs = self.get_playdefs();
return boost::algorithm::join(
hits
That reminds me â look at this method!
auto const get_playdefs() const return playdef;
Two things obviously wrong with it: One, it returns a const
-qualified object â that's always wrong, because it disables move semantics. Two, the name of the method is get_playdefs
, plural, but the thing it's actually getting is spelled playdef
, singular. Pick one name and stick to it!
The third thing wrong with this method is that it returns a potentially expensive vector by copy instead of by reference.
The fourth thing arguably wrong with it is that it returns the whole vector, but the only place you actually use this function, you want to fetch only a single element from the vector.
One last thing: It seems weird that you wrap up the mutable counter variable, self.get_val()
, into the same object instance as the immutable configuration (get_playdef()
and so on). It would make a lot more sense to me if you'd architected it something more like
std::string render(int value, const Config& config)
std::vector<std::string> hits;
for (auto&& rule : config.rules)
if (value % rule.divisor == 0)
hits.push_back(rule.word);
return hits.empty() ?
config.render_default(value) :
boost::algorithm::join(hits, " ");
so that the mutable loop counter (value
) was separated from the immutable configuration. Here's a worked example of what I mean.
I already looked into the static_assert: stackoverflow.com/questions/49829932/â¦
â JDà Âugosz
May 16 at 20:40
I use asymmetric spacing on assignment, which helps distinguish it visually from comparison and other operators. C++ does not have âÂÂmethodsâÂÂ.
â JDà Âugosz
May 16 at 20:43
"I already looked into the static_assert": Check out my Wandbox link, in both GCC and Clang, which shows your static_assert not stopping the spew of compiler errors about the member-initializer. wandbox.org/permlink/a4F4rMjJCfM8olnS
â Quuxplusone
May 16 at 21:35
value % rule.divisor == 0)
thatâÂÂs missing the point, and going back to post #1. Look at the 3rd paragraph of the question!
â JDà Âugosz
May 17 at 1:14
1
The residues is the state, that the object is there to hold. It simply refers to the (fixed) config so it knows what to do. Your proposed value type would be my existing class. The residues is tied to the specific factors being used, so it needs to be tied permanently to the configuration.
â JDà Âugosz
May 18 at 6:24
 |Â
show 1 more comment
up vote
3
down vote
accepted
Well, you already know that your code is Too Complicated, so I'll skip that part and just call out a few random things. :)
It's weird that you write function calls as f (x)
instead of f(x)
.
It's also weird that you write x= y
instead of x = y
.
template<typename R, typename>
Fizzer_t::Fizzer_t (const R& range_of_itemdefs)
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
residues.resize (playdef.size());
I'm pretty sure that by the time you get down to the static_assert
, the compiler will already have complained about the initialization of playdef
; so the static_assert
isn't really doing anything here.
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
Your use of curly-braces for initialization of std::vector
(with things-that-aren't-vector-elements) is problematic. Consider:
char x = "hello";
std::vector<std::string> vs std::begin(x), std::end(x) ; // oops!
versus:
char x = "hello";
std::vector<std::string> vs( std::begin(x), std::end(x) ); // fortunately fails to compile
The rule of thumb here is "use curlies when you know the exact elements you want to store into the container; use parentheses when you want to call a constructor which is acting as a factory function."
Your render
function is a bit hard to read. I'd write the same code like this:
std::string render(const Fizzer_t& self)
const auto& hits = self.get_hits();
if (hits.empty())
return std::to_string(self.get_val());
else
auto& playdefs = self.get_playdefs();
return boost::algorithm::join(
hits
That reminds me â look at this method!
auto const get_playdefs() const return playdef;
Two things obviously wrong with it: One, it returns a const
-qualified object â that's always wrong, because it disables move semantics. Two, the name of the method is get_playdefs
, plural, but the thing it's actually getting is spelled playdef
, singular. Pick one name and stick to it!
The third thing wrong with this method is that it returns a potentially expensive vector by copy instead of by reference.
The fourth thing arguably wrong with it is that it returns the whole vector, but the only place you actually use this function, you want to fetch only a single element from the vector.
One last thing: It seems weird that you wrap up the mutable counter variable, self.get_val()
, into the same object instance as the immutable configuration (get_playdef()
and so on). It would make a lot more sense to me if you'd architected it something more like
std::string render(int value, const Config& config)
std::vector<std::string> hits;
for (auto&& rule : config.rules)
if (value % rule.divisor == 0)
hits.push_back(rule.word);
return hits.empty() ?
config.render_default(value) :
boost::algorithm::join(hits, " ");
so that the mutable loop counter (value
) was separated from the immutable configuration. Here's a worked example of what I mean.
I already looked into the static_assert: stackoverflow.com/questions/49829932/â¦
â JDà Âugosz
May 16 at 20:40
I use asymmetric spacing on assignment, which helps distinguish it visually from comparison and other operators. C++ does not have âÂÂmethodsâÂÂ.
â JDà Âugosz
May 16 at 20:43
"I already looked into the static_assert": Check out my Wandbox link, in both GCC and Clang, which shows your static_assert not stopping the spew of compiler errors about the member-initializer. wandbox.org/permlink/a4F4rMjJCfM8olnS
â Quuxplusone
May 16 at 21:35
value % rule.divisor == 0)
thatâÂÂs missing the point, and going back to post #1. Look at the 3rd paragraph of the question!
â JDà Âugosz
May 17 at 1:14
1
The residues is the state, that the object is there to hold. It simply refers to the (fixed) config so it knows what to do. Your proposed value type would be my existing class. The residues is tied to the specific factors being used, so it needs to be tied permanently to the configuration.
â JDà Âugosz
May 18 at 6:24
 |Â
show 1 more comment
up vote
3
down vote
accepted
up vote
3
down vote
accepted
Well, you already know that your code is Too Complicated, so I'll skip that part and just call out a few random things. :)
It's weird that you write function calls as f (x)
instead of f(x)
.
It's also weird that you write x= y
instead of x = y
.
template<typename R, typename>
Fizzer_t::Fizzer_t (const R& range_of_itemdefs)
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
residues.resize (playdef.size());
I'm pretty sure that by the time you get down to the static_assert
, the compiler will already have complained about the initialization of playdef
; so the static_assert
isn't really doing anything here.
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
Your use of curly-braces for initialization of std::vector
(with things-that-aren't-vector-elements) is problematic. Consider:
char x = "hello";
std::vector<std::string> vs std::begin(x), std::end(x) ; // oops!
versus:
char x = "hello";
std::vector<std::string> vs( std::begin(x), std::end(x) ); // fortunately fails to compile
The rule of thumb here is "use curlies when you know the exact elements you want to store into the container; use parentheses when you want to call a constructor which is acting as a factory function."
Your render
function is a bit hard to read. I'd write the same code like this:
std::string render(const Fizzer_t& self)
const auto& hits = self.get_hits();
if (hits.empty())
return std::to_string(self.get_val());
else
auto& playdefs = self.get_playdefs();
return boost::algorithm::join(
hits
That reminds me â look at this method!
auto const get_playdefs() const return playdef;
Two things obviously wrong with it: One, it returns a const
-qualified object â that's always wrong, because it disables move semantics. Two, the name of the method is get_playdefs
, plural, but the thing it's actually getting is spelled playdef
, singular. Pick one name and stick to it!
The third thing wrong with this method is that it returns a potentially expensive vector by copy instead of by reference.
The fourth thing arguably wrong with it is that it returns the whole vector, but the only place you actually use this function, you want to fetch only a single element from the vector.
One last thing: It seems weird that you wrap up the mutable counter variable, self.get_val()
, into the same object instance as the immutable configuration (get_playdef()
and so on). It would make a lot more sense to me if you'd architected it something more like
std::string render(int value, const Config& config)
std::vector<std::string> hits;
for (auto&& rule : config.rules)
if (value % rule.divisor == 0)
hits.push_back(rule.word);
return hits.empty() ?
config.render_default(value) :
boost::algorithm::join(hits, " ");
so that the mutable loop counter (value
) was separated from the immutable configuration. Here's a worked example of what I mean.
Well, you already know that your code is Too Complicated, so I'll skip that part and just call out a few random things. :)
It's weird that you write function calls as f (x)
instead of f(x)
.
It's also weird that you write x= y
instead of x = y
.
template<typename R, typename>
Fizzer_t::Fizzer_t (const R& range_of_itemdefs)
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
static_assert (std::is_constructible_v<itemdef_t, typename boost::range_value<R>::type>);
residues.resize (playdef.size());
I'm pretty sure that by the time you get down to the static_assert
, the compiler will already have complained about the initialization of playdef
; so the static_assert
isn't really doing anything here.
: playdef Begin(range_of_itemdefs), End(range_of_itemdefs)
Your use of curly-braces for initialization of std::vector
(with things-that-aren't-vector-elements) is problematic. Consider:
char x = "hello";
std::vector<std::string> vs std::begin(x), std::end(x) ; // oops!
versus:
char x = "hello";
std::vector<std::string> vs( std::begin(x), std::end(x) ); // fortunately fails to compile
The rule of thumb here is "use curlies when you know the exact elements you want to store into the container; use parentheses when you want to call a constructor which is acting as a factory function."
Your render
function is a bit hard to read. I'd write the same code like this:
std::string render(const Fizzer_t& self)
const auto& hits = self.get_hits();
if (hits.empty())
return std::to_string(self.get_val());
else
auto& playdefs = self.get_playdefs();
return boost::algorithm::join(
hits
That reminds me â look at this method!
auto const get_playdefs() const return playdef;
Two things obviously wrong with it: One, it returns a const
-qualified object â that's always wrong, because it disables move semantics. Two, the name of the method is get_playdefs
, plural, but the thing it's actually getting is spelled playdef
, singular. Pick one name and stick to it!
The third thing wrong with this method is that it returns a potentially expensive vector by copy instead of by reference.
The fourth thing arguably wrong with it is that it returns the whole vector, but the only place you actually use this function, you want to fetch only a single element from the vector.
One last thing: It seems weird that you wrap up the mutable counter variable, self.get_val()
, into the same object instance as the immutable configuration (get_playdef()
and so on). It would make a lot more sense to me if you'd architected it something more like
std::string render(int value, const Config& config)
std::vector<std::string> hits;
for (auto&& rule : config.rules)
if (value % rule.divisor == 0)
hits.push_back(rule.word);
return hits.empty() ?
config.render_default(value) :
boost::algorithm::join(hits, " ");
so that the mutable loop counter (value
) was separated from the immutable configuration. Here's a worked example of what I mean.
answered May 16 at 7:16
Quuxplusone
9,73511451
9,73511451
I already looked into the static_assert: stackoverflow.com/questions/49829932/â¦
â JDà Âugosz
May 16 at 20:40
I use asymmetric spacing on assignment, which helps distinguish it visually from comparison and other operators. C++ does not have âÂÂmethodsâÂÂ.
â JDà Âugosz
May 16 at 20:43
"I already looked into the static_assert": Check out my Wandbox link, in both GCC and Clang, which shows your static_assert not stopping the spew of compiler errors about the member-initializer. wandbox.org/permlink/a4F4rMjJCfM8olnS
â Quuxplusone
May 16 at 21:35
value % rule.divisor == 0)
thatâÂÂs missing the point, and going back to post #1. Look at the 3rd paragraph of the question!
â JDà Âugosz
May 17 at 1:14
1
The residues is the state, that the object is there to hold. It simply refers to the (fixed) config so it knows what to do. Your proposed value type would be my existing class. The residues is tied to the specific factors being used, so it needs to be tied permanently to the configuration.
â JDà Âugosz
May 18 at 6:24
 |Â
show 1 more comment
I already looked into the static_assert: stackoverflow.com/questions/49829932/â¦
â JDà Âugosz
May 16 at 20:40
I use asymmetric spacing on assignment, which helps distinguish it visually from comparison and other operators. C++ does not have âÂÂmethodsâÂÂ.
â JDà Âugosz
May 16 at 20:43
"I already looked into the static_assert": Check out my Wandbox link, in both GCC and Clang, which shows your static_assert not stopping the spew of compiler errors about the member-initializer. wandbox.org/permlink/a4F4rMjJCfM8olnS
â Quuxplusone
May 16 at 21:35
value % rule.divisor == 0)
thatâÂÂs missing the point, and going back to post #1. Look at the 3rd paragraph of the question!
â JDà Âugosz
May 17 at 1:14
1
The residues is the state, that the object is there to hold. It simply refers to the (fixed) config so it knows what to do. Your proposed value type would be my existing class. The residues is tied to the specific factors being used, so it needs to be tied permanently to the configuration.
â JDà Âugosz
May 18 at 6:24
I already looked into the static_assert: stackoverflow.com/questions/49829932/â¦
â JDà Âugosz
May 16 at 20:40
I already looked into the static_assert: stackoverflow.com/questions/49829932/â¦
â JDà Âugosz
May 16 at 20:40
I use asymmetric spacing on assignment, which helps distinguish it visually from comparison and other operators. C++ does not have âÂÂmethodsâÂÂ.
â JDà Âugosz
May 16 at 20:43
I use asymmetric spacing on assignment, which helps distinguish it visually from comparison and other operators. C++ does not have âÂÂmethodsâÂÂ.
â JDà Âugosz
May 16 at 20:43
"I already looked into the static_assert": Check out my Wandbox link, in both GCC and Clang, which shows your static_assert not stopping the spew of compiler errors about the member-initializer. wandbox.org/permlink/a4F4rMjJCfM8olnS
â Quuxplusone
May 16 at 21:35
"I already looked into the static_assert": Check out my Wandbox link, in both GCC and Clang, which shows your static_assert not stopping the spew of compiler errors about the member-initializer. wandbox.org/permlink/a4F4rMjJCfM8olnS
â Quuxplusone
May 16 at 21:35
value % rule.divisor == 0)
thatâÂÂs missing the point, and going back to post #1. Look at the 3rd paragraph of the question!â JDà Âugosz
May 17 at 1:14
value % rule.divisor == 0)
thatâÂÂs missing the point, and going back to post #1. Look at the 3rd paragraph of the question!â JDà Âugosz
May 17 at 1:14
1
1
The residues is the state, that the object is there to hold. It simply refers to the (fixed) config so it knows what to do. Your proposed value type would be my existing class. The residues is tied to the specific factors being used, so it needs to be tied permanently to the configuration.
â JDà Âugosz
May 18 at 6:24
The residues is the state, that the object is there to hold. It simply refers to the (fixed) config so it knows what to do. Your proposed value type would be my existing class. The residues is tied to the specific factors being used, so it needs to be tied permanently to the configuration.
â JDà Âugosz
May 18 at 6:24
 |Â
show 1 more comment
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193294%2fover-engineered-fizzbuzz-17-style%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password