Tuesday, July 7, 2015

Digest for comp.lang.c++@googlegroups.com - 9 updates in 5 topics

pepstein5@gmail.com: Jul 07 08:02AM -0700

Textbooks often say things like "int& x = y; simply means that x is an alias for y."
 
However, this is ambiguous (to me). Take the following case. std::vector<int> vec; /* ..... */ int index = 5; int& ref = vec[index];
 
Is ref an alias for vec[5] or for vec[index]? In other words if index changes from 5 to 6, does ref now refer to vec[6]? Or does ref always refer to vec[5]?
 
Of course, I could just test it, but I would think there's a simple way of thinking about it that makes the answer apparent and unambiguous.
 
It's clear to me that if we write int& i = index; int& clearRef = vec[i]; then surely clearRef always refers to vec[index] however index changes. But the previous case is unclear to me.
 
Thank you very much for your help.
 
Paul
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jul 07 06:19PM +0200


> Is ref an alias for vec[5] or for vec[index]? In other words if index
> changes from 5 to 6, does ref now refer to vec[6]? Or does ref always refer
> to vec[5]?
 
Good question.
 
In C++ a reference refers to the result of the evaluation of the
initializer expression. But (as I recall) due to a sloppy formulation of
the specification of an early version of the Algol language, it ended up
with so called "call by name" where an argument expression effectively
was substituted into the called routine at each place the formal
argument name was used. It made for both interesting programming,
interesting compiler techniques, and superb inefficiency.
 
You can think of a C++ reference
 
T& r = expr;
 
as
 
T* const _p = &expr;
 
with a
 
#define r (*_p)
 
which explains most properties, except that since a macro definition
doesn't respect scopes it can't really do the job.
 
For more discussion based on that point of view see the FAQ items about
references, at <url: https://isocpp.org/wiki/faq/references>.
 
Where you need something akin to a reference to an expression, like an
Algol call by name formal argument, you can use a named C++ lambda
expression.
 
vector<int> v;
int i;
auto const r = [&]() -> int& { return v[i]; }
 
i = 42; r() = 12345;
 
 
Cheers & hth.,
 
- Alf
 
--
Using Thunderbird as Usenet client, Eternal September as NNTP server.
"Öö Tiib" <ootiib@hot.ee>: Jul 07 10:16AM -0700

On Tuesday, 7 July 2015 18:03:41 UTC+3, peps...@gmail.com wrote:
 
Alf already answered rest of it well enough.
 
> Of course, I could just test it, but I would think there's a simple way of thinking
> about it that makes the answer apparent and unambiguous.
 
It is always worth testing when you think you understood something
correctly. If you suspect that you did not understand it then the tests
may also lie to you. Better ask or read it over from other textbook.
 
For example you might want to be particularly careful with references
to elements of vector since lot of operations that are done later with
vector may invalidate references. Invalid references will often appear
to work with existing C++ compilers.
 
Somewhat safer is to use iterators to elements of vector. These tend to
have debug versions that fail illegal usages with runtime errors instead
of appearing to work. So if you test with such debug version then it is
less likely to become mislead by test results.
Vir Campestris <vir.campestris@invalid.invalid>: Jul 07 09:29PM +0100

On 07/07/2015 18:16, Öö Tiib wrote:
> to elements of vector since lot of operations that are done later with
> vector may invalidate references. Invalid references will often appear
> to work with existing C++ compilers.
 
They'll work up to the moment you are demonstrating your code to someone
important. DAMHIK... And I don't expect that to change. Making all
iterators fail, even if the memory is used for something else, is bound
to be slow.
 
> have debug versions that fail illegal usages with runtime errors instead
> of appearing to work. So if you test with such debug version then it is
> less likely to become mislead by test results.
 
In my experience even the debug ones won't fail _all_ the illegal
usages. But they do help a lot.
 
Andy
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jul 07 06:42AM +0200

On 06-Jul-15 7:53 AM, Tobias Müller wrote:
>> (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.
 
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;

No comments: