Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Optional Code - a Better Way?

101 views
Skip to first unread message

MikeCopeland

unread,
Jul 5, 2015, 8:05:20 PM7/5/15
to
I'm developing an application which contains a lot of user interface
dialog. As I test it, I'm forced to repeat an increasing number of
prompt/data response sets. For the moment, I'd like to have the program
use a fixed set of data as I evolve new functions.
Is there a better way to do something like:
#define TESTED true;
...
#ifndef TESTED
{
ossz.str(""), ossz << "Enter RaceId (3-9 characters) for race #"
<< nx1 + 1 << ":";
wProm(2, PRLINE, HINORM, ossz.str()), readStr(tEvent);
}
#endif
#ifdef TESTED
{ tEvent = "eds145a"; }
#endif
(Note: wProm & readStr are my functions.) Please advise. TIA


---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus

Message has been deleted

Victor Bazarov

unread,
Jul 5, 2015, 8:26:07 PM7/5/15
to
On 7/5/2015 8:04 PM, MikeCopeland wrote:
> I'm developing an application which contains a lot of user interface
> dialog. As I test it, I'm forced to repeat an increasing number of
> prompt/data response sets. For the moment, I'd like to have the program
> use a fixed set of data as I evolve new functions.
> Is there a better way to do something like:
> #define TESTED true;
> ...
> #ifndef TESTED
> {
> ossz.str(""), ossz << "Enter RaceId (3-9 characters) for race #"
> << nx1 + 1 << ":";
> wProm(2, PRLINE, HINORM, ossz.str()), readStr(tEvent);
> }
> #endif
> #ifdef TESTED
> { tEvent = "eds145a"; }
> #endif
> (Note: wProm & readStr are my functions.) Please advise. TIA

I think a better way is to use a unit test framework of some kind.
There are several on the market. There you define functions that call
your function with specific argument and verify that the return code (or
the behavior) is according to what you expect. Those are only executed
(exercised) when you start your application with specific command-line
argument, for instance, or even themselves are a special executable to
exercise your library. Don't remove anything. Just make sure that each
test calls only a specific part of your code.

It's possible that I misunderstand the question, of course; like Stefan
says, your request is a bit unclear.

V
--
I do not respond to top-posted replies, please don't ask

MikeCopeland

unread,
Jul 5, 2015, 8:41:37 PM7/5/15
to
In article <guess-2015...@ram.dialup.fu-berlin.de>, r...@zedat.fu-
berlin.de says...
>
> >#ifndef TESTED
> > {
> > ossz.str(""), ossz << "Enter RaceId (3-9 characters) for race #" << nx1 + 1 << ":";
> > wProm(2, PRLINE, HINORM, ossz.str()), readStr(tEvent);
> > }
> >#endif
> >#ifdef TESTED
> > { tEvent = "eds145a"; }
> >#endif
>
> I don't understand the question, so this is just a wild guess:
>
> #ifdef BLOCK
> #error BLOCK is already defined.
> #endif
> #ifdef TESTED
> #define BLOCK(prompt,name,value) { name = value; }
> #else
> #define BLOCK(prompt,name,value) \
> { ossz.str(""), ossz << prompt << ":"; \
> wProm( 2, PRLINE, HINORM, ossz.str() ), readStr( name ); }
> #endif
> BLOCK( "Enter RaceId (3-9 characters) for race #" << nx1 + 1, tEvent, "eds145a" )

I want compile units to generate certain code (that which forces
fixed response data to various prompts)...and NOT generate the code
which, when I test it, skips the prompt/response code. Then, when I
have the program reasonably debugged (and all functionality more or less
working) I can change the #define value and execute the "production
code".
That is, I find it needlessly tedious for me to key in fixed "test
values" as I add and test new functionality. Years ago there was a DOS
capability called "piping" that would run a set of responses through a
program. I'd like to mimic that behavior for my C++ IDE development. 8
<}}
If there's a way to do this (in VS2013), I'd like to know how.
Message has been deleted

Tobias Müller

unread,
Jul 6, 2015, 1:55:08 AM7/6/15
to
MikeCopeland <mrc...@cox.net> wrote:
> I'm developing an application which contains a lot of user interface
> dialog. As I test it, I'm forced to repeat an increasing number of
> prompt/data response sets. For the moment, I'd like to have the program
> use a fixed set of data as I evolve new functions.
> Is there a better way to do something like:
> #define TESTED true;
> ...
> #ifndef TESTED
> {
> ossz.str(""), ossz << "Enter RaceId (3-9 characters) for race #"
> << nx1 + 1 << ":";
> wProm(2, PRLINE, HINORM, ossz.str()), readStr(tEvent);
> }
> #endif
> #ifdef TESTED
> { tEvent = "eds145a"; }
> #endif
> (Note: wProm & readStr are my functions.) Please advise. TIA

Stop writing spaghetti code, sparate the logic from the user interface.
Then write unittests that use fixed data to test the logic part.

Tobi

Andrea Venturoli

unread,
Jul 6, 2015, 2:27:54 AM7/6/15
to
On 07/06/15 02:04, MikeCopeland wrote:
> I'm developing an application which contains a lot of user interface
> dialog...

Hello.

At first I thought you were talking about GUI dialogs, but then I think
what all is about is text prompts, right?

Being a UNIX guy, I would build some test suite around expect
(http://expect.sourceforge.net/), perhaps, as Victor said, by exploiting
some readily available unit test framework.
Maybe that's worth trying on Windows too.

I double, however, Tobias' suggestion: you should *probably* separate
the logic from the interface and test the former ("probably" is there
because I don't really know your situation).

bye
av.

Alf P. Steinbach

unread,
Jul 7, 2015, 12:43:14 AM7/7/15
to
I second that.

But I think that given the shown code, an example of getting into such
mess, and out of it again, could be useful.

For this example I'll use the following i/o support functions:

-----------------------------------------------------------------------
<file "exception_util.hpp">
#pragma once
#include <stdexcept>

namespace my{
using std::runtime_error;
using std::string;

inline
auto fail( string const& message )
-> bool
{ throw runtime_error( message ); }
} // namespace my
</file>

-----------------------------------------------------------------------
<file "io_util.hpp">
#pragma once
#include "exception_util.hpp"

#include <iostream>
#include <string>

namespace my {
using std::cin;
using std::cout;
using std::ostream;
using std::getline;
using std::string;

inline
auto int_from_user()
-> int
{
string line;
getline( cin, line ) || fail( "Input failed" );
return stoi( line );
}

inline
auto int_from_user( string const prompt )
-> int
{
cout << prompt;
return int_from_user();
}

class Output_value_list
{
private:
ostream& stream_;
bool is_first_value_;

using This = Output_value_list;
auto operator=( This const& ) -> This& = delete;

public:
template< class Value >
void operator()( Value const& value )
{
if( not is_first_value_ ) { stream_ << ", "; }
stream_ << value;
is_first_value_ = false;
}

Output_value_list( ostream& stream )
: stream_( stream )
, is_first_value_( true )
{}
};

} // namespace my
</file>


*** STEP 0

We write a little program that outputs a mysterious (or not so very
mysterious) sequence of numbers. The point is just to have a not
completely trivial program to discuss and outfit with testing.

-----------------------------------------------------------------------
<file "0/main.cpp">
#include "io_util.hpp"
using my::int_from_user;
using namespace std;

auto main() -> int
{
int const ddx = int_from_user( "Delta? " );
int const n = int_from_user( "Number of numbers? " );

int x = 0;
int dx = 0;
for( int count = 1; count <= n; ++count )
{
dx += ddx; x += dx;
if( count > 1 ) { cout << ", "; }
cout << x;
}
cout << endl;
}
</file>


*** STEP 1

We choose the worst possible ways to add some unit testing to code,
namely, we'll test generated text rather than the data itself, and we'll
do this by sprinkling preprocessor directives all over the code in order
to have just one source code file that's everything and that can be
built as either the original program, or as a tester program:

-----------------------------------------------------------------------
<file "1/main.cpp">
#include "io_util.hpp"
using my::int_from_user;

#include <assert.h> // assert
#include <iostream>
#include <stdexcept> // std::runtime_error
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
#include <string> // std::string
#include <sstream> // std::ostringstream;
#include <utility> // std::begin, std::end
using namespace std;

#define IS_UNUSED( arg ) (void) arg; struct arg

auto main( int n_args, char* args[] ) -> int
{
#ifdef TEST
assert( n_args == 2 );
int const ddx = stoi( args[1] );
int const n = 7;
ostringstream output;
#else
IS_UNUSED( n_args ); IS_UNUSED( args );
int const ddx = int_from_user( "Delta? " );
int const n = int_from_user( "Number of numbers? " );
ostream& output = cout;
#endif

int x = 0;
int dx = 0;
for( int count = 0; count < n; ++count )
{
dx += ddx; x += dx;
if( count > 0 ) { output << ", "; }
output << x;
}
output << endl;

#ifdef TEST
string const expecteds[] =
{
"1, 3, 6, 10, 15, 21, 28\n",
"2, 6, 12, 20, 30, 42, 56\n",
"3, 9, 18, 30, 45, 63, 84\n"
};
assert( ddx <= end( expecteds ) - begin( expecteds ) );
string const& expected = expecteds[ddx - 1];

bool const ok = (output.str() == expected);
cout << (ok? "OK" : "Failed") << " (ddx = " << ddx << ")" << endl;
if( not ok )
{
cout << "Expected '" << expected << "' but got '" <<
output.str() << "'" << endl;
}
return (ok? EXIT_SUCCESS : EXIT_FAILURE);
#endif
}
</file>

One main problem with this is that it looks complex.

That's a problem because maintenance is about 80% of all programming,
and apparent complexity makes the eyes of the maintainer glaze over.

Getting down to nitty-gritty concrete problems, then, for example, any
change of the output format requires updating the test expectations. The
test code even depends on the internal variable names in the code to be
tested. And not just that, but the main code's stream reference called
"output" is there solely in order to support passing results to the
testing code.


*** STEP 2

Instead of conditional compilation and one single file, the function to
be tested should be put in a module of its own, either separately
compiled or as a header only module. Then a main program that doesn't
test, is easily implemented. And likewise, a program that only tests, is
also easily implemented, independently of the first one.

Since the code generates a sequence of numbers a natural choice is to
factor it out as a function that returns a "vector<int>".

It's not the most general choice available, but it's simple and
convenient until one needs something less memory-hungry:

-----------------------------------------------------------------------
<file "2/number_generator.hpp">
#pragma once
#include <vector>

namespace my {
using std::vector;

inline
auto generate_numbers( int const delta, int const n )
-> vector<int>
{
vector<int> result;

int x = 0;
int dx = 0;
for( int i = 1; i <= n; ++i )
{
dx += delta;
x += dx;
result.push_back( x );
}
return result;
}
} // namespace my
</file>

Now the ordinary main program can be pretty simple:

-----------------------------------------------------------------------
<file "2/main.cpp">
#include "number_generation.hpp"
#include "io_util.hpp"
using my::Output_value_list;

auto main() -> int
{
using my::int_from_user;
using my::generate_numbers;

int const ddx = int_from_user( "Delta? " );
int const n = int_from_user( "Number of numbers? " );

Output_value_list output( cout );
for( int const x : generate_numbers( ddx, n ) )
{
output( x );
}
cout << endl;
}
</file>

The testing program is only slightly less simple:

-----------------------------------------------------------------------
<file "2/test.cpp">
#include "number_generation.hpp"
#include "io_util.hpp"
using my::Output_value_list;

#include <assert.h> // assert
#include <map> // std::map
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
using namespace std;

auto operator<<( ostream& stream, vector<int> const& v )
-> ostream&
{
Output_value_list output( stream );
for( int const x : v ) { output( x ); }
return stream;
}

auto main( int n_args, char* args[] ) -> int
{
using my::generate_numbers;

assert( n_args == 2 );
int const ddx = stoi( args[1] );
int const n = 7;
vector<int> const numbers = generate_numbers( ddx, n );

map< int, vector<int> > const expecteds =
{
{1, {1, 3, 6, 10, 15, 21, 28}},
{2, {2, 6, 12, 20, 30, 42, 56}},
{3, {3, 9, 18, 30, 45, 63, 84}}
};
bool const ok = (numbers == expecteds.at( ddx ));

cout << (ok? "OK" : "Failed") << " (ddx = " << ddx << ")" << endl;
if( not ok )
{
cout << "Expected '" << expecteds.at( ddx ) << "' but got '" <<
numbers << "'" << endl;
}
return (ok? EXIT_SUCCESS : EXIT_FAILURE);
}
</file>

If the code is changed so that arbitrary long sequences need to be
tested, then one way to deal with that is that instead of returning a
"vector<int>", the basic function just calls a supplied function with
each position and value, as they're encountered. But this posting is
already long enough, so I choose to not show that here. Just a final
note: I have intentionally refrained from generalizing things, like, the
operator<< here is not very general, and that's IMHO as it should be for
example code -- just very concrete.

Also, it can be a good idea to use Someone Else(TM)'s unit testing
framework. E.g. the Google one.


Cheers & hth.,

- Alf

--
Using Thunderbird as Usenet client, Eternal September as NNTP server.

Richard

unread,
Jul 7, 2015, 2:38:34 PM7/7/15
to
[Please do not mail me a copy of your followup]

MikeCopeland <mrc...@cox.net> spake the secret code
<MPG.300374118...@news.eternal-september.org> thusly:

> I'm developing an application which contains a lot of user interface
>dialog. As I test it, I'm forced to repeat an increasing number of
>prompt/data response sets.

Use the Humble Dialog pattern and separate the logic of the dialog from
the presentation of the dialog. An example of how to code the
functionality of a UI using the Humble Dialog pattern can be found in
part 4 of my tutorial on test-driven development in C++:
<https://legalizeadulthood.wordpress.com/2009/07/05/c-unit-tests-with-boost-test-part-4/>

This same example was reworked using the Turtle Mock library for C++
Now! 2014:
<https://github.com/boostcon/cppnow_presentations_2014/tree/master/files/test_driven>
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

David Harmon

unread,
Jul 9, 2015, 1:19:18 AM7/9/15
to
On Sun, 5 Jul 2015 17:41:24 -0700 in comp.lang.c++, MikeCopeland
<mrc...@cox.net> wrote,
> I want compile units to generate certain code (that which forces
>fixed response data to various prompts)...and NOT generate the code
>which, when I test it, skips the prompt/response code. Then, when I
>have the program reasonably debugged (and all functionality more or less
>working) I can change the #define value and execute the "production
>code".

Just be aware that testing *different* code from what you are going to
release is a famous trap.


Richard

unread,
Jul 9, 2015, 12:21:09 PM7/9/15
to
[Please do not mail me a copy of your followup]

"Newsgroup only please, address is no longer replyable."
<b...@example.invalid> spake the secret code
<H4OdnXgiyrTdmQPI...@earthlink.com> thusly:
Agreed. This is why it is best to keep test code in separate source
files that link against production code in order to create a test
executable. If you're back-filling tests onto legacy code, this may
mean that you need to repackage existing production code into a library
so that it can be linked into a test executable. I've done this a
bunch of times; it isn't that difficult.

From a package dependency perspective, you want something like this:

<production static library>
^ ^
/|\ /|\
| |
| +-----------------------+
| |
<production executable> <test executable>

Ideally the code in <production executable> consists only of a single
delegating implementation of main():

#include "production.h"

int main(int argc, char *argv[])
{
return production_main(argc, argv);
}

This allows you to unit test command-line argument handling in main.
Message has been deleted
Message has been deleted

Tobias Müller

unread,
Jul 9, 2015, 4:50:51 PM7/9/15
to
Stefan Ram <r...@zedat.fu-berlin.de> wrote:
> Supersedes: <ASCII-Art-20...@ram.dialup.fu-berlin.de>
> [corrected first line ending in "WI"]

That should already answer your question below.

> [...]
> How is it possible that now even participants of a technical
> newsgroup can't even get monospaced ASCII-art right anymore?
> [...]

Tobi
0 new messages