473,770 Members | 2,126 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Traversing members of a class with a pointer

While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
....

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
....
Badness badness;

std::string *it = &badness.m_begi n;

for (++it; it != &badness.m_e nd; ++it) {
std::cout << it->c_str() << std::endl;
}

It just looks wrong. It depends on all of the members of class
or structure to be consistent so its bad from a maintenance
standpoint. It seems evil to use a pointer beyond the specific
object for which it is defined.

Looking wrong and evil, though, are not rational arguments that
would allow me to convince my technical lead that this code
needs to be changed sooner than later.

Anyone have good talking points?

/Glen
Dec 9 '06
14 1883
Gavin Deane wrote:
Pete Becker wrote:
>Gavin Deane wrote:
>>Of course, formally the code is broken by
definition.
For a somewhat artificial definition of "broken." If it does what it's
supposed to do most people wouldn't consider it broken.

It does what it's supposed to do, but how do I know that?
You test it.
Where is the
specification that defines what behavior that code should have?
If I am relying on undefined behaviour, the simple fact that there is
nowhere I can go to read documentation that explains what the behaviour
should be makes me uncomfortable as an engineer.
That's not necessarily the case. The standard does not prohibit compiler
vendors from documenting behavior that is formally undefined. But that
aside...
Operating outside the
world of specification is moving away from sound engineering principles
and towards voodoo.
Only if you don't test that the code does what you need it to do.
I would suggest that a software engineer should
only do that with good reason.
That's a different issue.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
Dec 10 '06 #11


On Dec 10, 5:46 am, Pete Becker <p...@versatile coding.comwrote :
Glen Dayton wrote:
Pete Becker wrote:
Glen Dayton wrote:
>Looking wrong and evil, though, are not rational arguments that would
allow me to convince my technical lead that this code needs to be
changed sooner than later.
If it works, it works. The behavior isn't defined by the C++ standard,
but I haven't run into a compiler where it won't do just what it ooks
like it does.
That it is undefined by the standard is one problem. It means at a
future date the code will stop working.
No, it means that at a future date the code might stop working. That's
not to say that it's a good way to write code, but hyperventilatin g is
not a good way to start a design.
If it works, it works, but surely that isn't the only criteria for
whether code is acceptable.
You didn't quote from this poster what I think is the most compelling
reason for modifying this code, which is that a future maintainer may
modify the class in what seems to be an innocuous manner, but breaks
the code that assumes contiguous layout. I don't think being concerned
about that merits the label 'hyperventilati ng'.

Dec 11 '06 #12
Salt_Peter wrote:
Glen Dayton wrote:
>While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
...

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
...
Badness badness;

std::string *it = &badness.m_begi n;

for (++it; it != &badness.m_e nd; ++it) {
std::cout << it->c_str() << std::endl;
}

It just looks wrong. It depends on all of the members of class
or structure to be consistent so its bad from a maintenance
standpoint. It seems evil to use a pointer beyond the specific
object for which it is defined.

Looking wrong and evil, though, are not rational arguments that
would allow me to convince my technical lead that this code
needs to be changed sooner than later.

Anyone have good talking points?

/Glen

Loose the pointers.
Can you not do something like this? Note: you must keep the
std::vector< std::string container at the top of the struct in order
to guarentee that it is initialized before the references.

#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <iterator>

struct Badness {
private:
std::vector< std::string vs;
public:
std::string& m_begin;
std::string& m_param1;
std::string& m_param2;
std::string& m_end;
typedef std::vector< std::string >::iterator baditerator;
typedef std::vector< std::string >::const_iterat or const_baditerat or;

Badness() : vs(4),
m_begin(vs[0]), m_param1(vs[1]),
m_param2(vs[2]), m_end(vs[3])
{ }
baditerator begin() { return vs.begin(); }
baditerator end() { return vs.end(); }

// friend
friend std::ostream&
operator<<(std: :ostream& os, const Badness& r_bad)
{
std::copy( r_bad.vs.begin( ),
r_bad.vs.end(),
std::ostream_it erator< std::string >(os, "\n") );
return os;
}
};

int main()
{
Badness badness;
badness.m_begin = "string m_begin";
badness.m_param 1 = "string m_param1";
badness.m_param 2 = "string m_param2";
badness.m_end = "string m_end";

for (Badness::badit erator iter = badness.begin() ;
iter != badness.end();
++iter)
{
std::cout << *iter << std::endl;
}

std::cout << "\n using op<<...\n";
std::cout << badness;
}
Thanks.

Instead of using a std::vector<whi ch requires run-time
initialization, how about just using a static array of member
pointers? That way the initialization is done at load time and
the normal type safety applies:

struct Sample
{
std::string m_param1;
std::string m_param2;

static std::string Sample::* params[2];

// Provide a convenient means of stepping through
class iterator
{
private:
iterator() : m_dex(0), m_base(NULL) {}
unsigned int m_dex;
Sample *m_base;

public:
iterator(Sample *base) : m_dex(0), m_base(base) {}
iterator(Sample *base, unsigned int dex) : m_dex(dex),
m_base(base) {}

std::string& operator*()
{ return m_base->*Sample::param s[m_dex]; }

std::string* operator->()
{ return &(m_base->*Sample::param s[m_dex]); }
iterator& operator++()
{ ++m_dex; return *this; }

bool operator==(cons t iterator& other)
{ return m_base == other.m_base && m_dex == other.m_dex; }

bool operator!=(cons t iterator& other)
{ return m_base != other.m_base || m_dex != other.m_dex; }
};

Sample();
iterator begin() { return iterator(this); }
iterator end()
{ return iterator(this,
sizeof(Sample:: params)/sizeof(Sample:: params[0])); }

};
....

for (Sample::iterat or iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Glen Dayton
Dec 13 '06 #13

Glen Dayton wrote:
Salt_Peter wrote:
Glen Dayton wrote:
While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
...

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
...
Badness badness;

std::string *it = &badness.m_begi n;

for (++it; it != &badness.m_e nd; ++it) {
std::cout << it->c_str() << std::endl;
}

It just looks wrong. It depends on all of the members of class
or structure to be consistent so its bad from a maintenance
standpoint. It seems evil to use a pointer beyond the specific
object for which it is defined.

Looking wrong and evil, though, are not rational arguments that
would allow me to convince my technical lead that this code
needs to be changed sooner than later.

Anyone have good talking points?

/Glen
Loose the pointers.
Can you not do something like this? Note: you must keep the
std::vector< std::string container at the top of the struct in order
to guarentee that it is initialized before the references.

#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <iterator>

struct Badness {
private:
std::vector< std::string vs;
public:
std::string& m_begin;
std::string& m_param1;
std::string& m_param2;
std::string& m_end;
typedef std::vector< std::string >::iterator baditerator;
typedef std::vector< std::string >::const_iterat or const_baditerat or;

Badness() : vs(4),
m_begin(vs[0]), m_param1(vs[1]),
m_param2(vs[2]), m_end(vs[3])
{ }
baditerator begin() { return vs.begin(); }
baditerator end() { return vs.end(); }

// friend
friend std::ostream&
operator<<(std: :ostream& os, const Badness& r_bad)
{
std::copy( r_bad.vs.begin( ),
r_bad.vs.end(),
std::ostream_it erator< std::string >(os, "\n") );
return os;
}
};

int main()
{
Badness badness;
badness.m_begin = "string m_begin";
badness.m_param 1 = "string m_param1";
badness.m_param 2 = "string m_param2";
badness.m_end = "string m_end";

for (Badness::badit erator iter = badness.begin() ;
iter != badness.end();
++iter)
{
std::cout << *iter << std::endl;
}

std::cout << "\n using op<<...\n";
std::cout << badness;
}
Thanks.

Instead of using a std::vector<whi ch requires run-time
initialization, how about just using a static array of member
pointers? That way the initialization is done at load time and
the normal type safety applies:
Run-time initialization has huge advantages and a design involving
static arrays will fail.
A static is essentially a singleton but not accross compilation units
<ewww>.
C++ has a solution, however, statics should be replaced with
namespaces.
>
struct Sample
{
std::string m_param1;
std::string m_param2;

static std::string Sample::* params[2];
There is no this in a static variable or container, therefore:

static std::string* params[2];

Static arrays can't be initialized in ctor initialization lists and you
can't control when these are allocated. Note also, that statics are
confined to their compilation unit. So a static in that source isn't
the same static variable as in the other compilation unit. Arrays are
evil, static arrays are downright dangerous accross compilation units.
Debugging these is a nightmare.
>
// Provide a convenient means of stepping through
class iterator
{
private:
iterator() : m_dex(0), m_base(NULL) {}
unsigned int m_dex;
Sample *m_base;

public:
iterator(Sample *base) : m_dex(0), m_base(base) {}
iterator(Sample *base, unsigned int dex) : m_dex(dex),
m_base(base) {}

std::string& operator*()
{ return m_base->*Sample::param s[m_dex]; }
{ return *m_base->params[m_dex]; }
>
std::string* operator->()
{ return &(m_base->*Sample::param s[m_dex]); }
{ return m_base->params[m_dex]; }
>

iterator& operator++()
{ ++m_dex; return *this; }

bool operator==(cons t iterator& other)
{ return m_base == other.m_base && m_dex == other.m_dex; }

bool operator!=(cons t iterator& other)
{ return m_base != other.m_base || m_dex != other.m_dex; }
};

Sample();
Sample(std::str ing s1, std::string s2)
: m_param1(s1), m_param2(s2)
{
params[0] = &m_param1; // not allowed in init lists
params[1] = &m_param2;
};

iterator begin() { return iterator(this); }
iterator end()
{ return iterator(this,
sizeof(Sample:: params)/sizeof(Sample:: params[0])); }

};
// required:
std::string* Sample::params[2];
...

for (Sample::iterat or iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Glen Dayton
// Now watch how nasty this gets, not to mention insidious:

int main()
{
Sample sample("string 1", "string 2"); // sample
for (Sample::iterat or iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Sample another("string 3", "string 4"); // another
for (Sample::iterat or iter = another.begin() ;
iter != another.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

// print the original sample strings - sample
for (Sample::iterat or iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}
}

/*
string 1
string 2
string 3
string 4
string 3 // the array no longer points to the original strings
string 4
*/

Dec 13 '06 #14
Salt_Peter wrote:
Glen Dayton wrote:
>Salt_Peter wrote:
>>Glen Dayton wrote:
While looking at some old code I ran across a snippet that
increments a pointer to access different members of a structure:
...

struct Badness {
std::string m_begin;
std::string m_param1;
std::string m_param2;
std::string m_end;

Badness();
};
...
Badness badness;

std::strin g *it = &badness.m_begi n;

for (++it; it != &badness.m_e nd; ++it) {
std::cout << it->c_str() << std::endl;
}
>>Loose the pointers.
Can you not do something like this? Note: you must keep the
std::vector < std::string container at the top of the struct in order
to guarentee that it is initialized before the references.

#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <iterator>

struct Badness {
private:
std::vector< std::string vs;
public:
std::string& m_begin;
std::string& m_param1;
std::string& m_param2;
std::string& m_end;
typedef std::vector< std::string >::iterator baditerator;
typedef std::vector< std::string >::const_iterat or const_baditerat or;

Badness() : vs(4),
m_begin(vs[0]), m_param1(vs[1]),
m_param2(vs[2]), m_end(vs[3])
{ }
baditerator begin() { return vs.begin(); }
baditerator end() { return vs.end(); }

// friend
friend std::ostream&
operator<<(std: :ostream& os, const Badness& r_bad)
{
std::copy( r_bad.vs.begin( ),
r_bad.vs.end(),
std::ostream_it erator< std::string >(os, "\n") );
return os;
}
};
>Instead of using a std::vector<whi ch requires run-time
initialization , how about just using a static array of member
pointers? That way the initialization is done at load time and
the normal type safety applies:

Run-time initialization has huge advantages and a design involving
static arrays will fail.
A static is essentially a singleton but not accross compilation units
<ewww>.
C++ has a solution, however, statics should be replaced with
namespaces.
>struct Sample
{
std::string m_param1;
std::string m_param2;

static std::string Sample::* params[2];

There is no this in a static variable or container, therefore:

static std::string* params[2];

Static arrays can't be initialized in ctor initialization lists and you
can't control when these are allocated. Note also, that statics are
confined to their compilation unit. So a static in that source isn't
the same static variable as in the other compilation unit. Arrays are
evil, static arrays are downright dangerous accross compilation units.
Debugging these is a nightmare.
> // Provide a convenient means of stepping through
class iterator
{
private:
iterator() : m_dex(0), m_base(NULL) {}
unsigned int m_dex;
Sample *m_base;

public:
iterator(Sample *base) : m_dex(0), m_base(base) {}
iterator(Sample *base, unsigned int dex) : m_dex(dex),
m_base(base) {}

std::string& operator*()
{ return m_base->*Sample::param s[m_dex]; }

{ return *m_base->params[m_dex]; }
> std::string* operator->()
{ return &(m_base->*Sample::param s[m_dex]); }

{ return m_base->params[m_dex]; }
>>
iterator& operator++()
{ ++m_dex; return *this; }

bool operator==(cons t iterator& other)
{ return m_base == other.m_base && m_dex == other.m_dex; }

bool operator!=(cons t iterator& other)
{ return m_base != other.m_base || m_dex != other.m_dex; }
};

Sample();

Sample(std::str ing s1, std::string s2)
: m_param1(s1), m_param2(s2)
{
params[0] = &m_param1; // not allowed in init lists
params[1] = &m_param2;
};
Whoa! params is an array of pointers to members! You've
initialized it incorrectly. See my oops below.

>
> iterator begin() { return iterator(this); }
iterator end()
{ return iterator(this,
sizeof(Sample: :params)/sizeof(Sample:: params[0])); }

};

// required:
std::string* Sample::params[2];

Sorry, left off the initialization:
std::string Sample::* Sample::params[2] =
{&Sample::m_par am1, &Sample::m_para m2};

>
>...

for (Sample::iterat or iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Glen Dayton

// Now watch how nasty this gets, not to mention insidious:

int main()
{
Sample sample("string 1", "string 2"); // sample
for (Sample::iterat or iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

Sample another("string 3", "string 4"); // another
for (Sample::iterat or iter = another.begin() ;
iter != another.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}

// print the original sample strings - sample
for (Sample::iterat or iter = sample.begin();
iter != sample.end();
++iter)
{
std::cout << iter->c_str() << std::endl;
}
}

/*
string 1
string 2
string 3
string 4
string 3 // the array no longer points to the original strings
string 4
*/
Thank you for your time analyzing my little program. In the
future I will include the complete source to help prevent
misunderstandin gs like this.

I'm surprised your code compiled. The iterator uses the pointer
to members which must be used with a base pointer. I
specifically used pointers to members so I could have diffent
instances of the same class. Notice the ->* syntax in the iterator.

/Glen Dayton

Dec 14 '06 #15

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
1270
by: Christian Nolte | last post by:
I want to implement dynamic calling of interface methods using pointers to members and have some trouble to get it right. An example of what I am talking about looks like that: /////////////////////////////////////////////// class StdInterface { public: int func(void) {return 1;} int func2(void) {return 2;} };
4
1772
by: Oystein Haare | last post by:
Is it best to declare class member objects as pointers or not pointers? class A { public: A(int i); //.... };
4
4739
by: Steven T. Hatton | last post by:
I mistakenly set this to the comp.std.c++ a few days back. I don't believe it passed the moderator's veto - and I did not expect or desire anything different. But the question remains: ISO/IEC 14882:2003(E) §8.5 says:   To zero-initialize an object of type T means: 5   -- if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
5
2185
by: pmatos | last post by:
Hi all, I have a style question. I've been for long programming in Lisp-like languages and C when I need a low-level language. Now, I'm programming for professional reasons in C++ (which I had programmed in before but only in college) and I'm getting to like it, however I'm having some issues with style. For example, If I have an object which I define as a private member of a class, for
6
2472
by: lovecreatesbeauty | last post by:
Hello Experts, Why static data members can be declared as the type of class which it belongs to? Inside a class, non-static data members such as pointers and references can be declared as type of its own class. Non-static data members can not be declared as type of its own class (excluding pointers and references).
4
1248
by: Paolo Capriotti | last post by:
Compiling the following code (gcc 3.3.6), I get an invalid conversion error. Is there a valid reason why this conversion can't be performed? --- class A {}; class B : public A {}; class T {
3
5796
by: Ryan Steckler | last post by:
I found this behavior while trying to implement a singleton class. Below is a somewhat more straight forward example, though admittedly less useful in real life. The basic problem is that when a static property of a class is called (StaticInit.MyProperty), the property code executes BEFORE the static members are initialized. This would lead one to assume that the static members would equate to null, but that isn't the case either. They...
14
2644
by: lovecreatesbea... | last post by:
Could you tell me how many class members the C++ language synthesizes for a class type? Which members in a class aren't derived from parent classes? I have read the book The C++ Programming Language, but there isn't a detail and complete description on all the class members, aren't they important to class composing? Could you explain the special class behavior in detail? Thank you very much.
6
8161
by: Bhawna | last post by:
I am into c++ code maintenance for last 3-4 years but recently I am put into design phase of a new project. Being a small comapany I dont have enough guidance from seniors. Currently I am into a situation where I am implementing base class functions by including a pointer to subclass member in base class. Reason being functionality is common for subclasses but the members are common within subclass only (static member of subclass) but...
0
9454
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10260
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10101
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
9906
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8933
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7456
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6712
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5354
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
3
2850
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.