Accessing elements of static array by name

  • Thread starter Paul Brettschneider
  • Start date
P

Paul Brettschneider

Hello,

I have a global static array of structs and want to access a given
element using an identifier. I don't want to use the element subscript,
because it will change if I insert elements before the element I
want to access. In assembler I would simply add a label in front
of the element, but this doesn't work in C++.

The following programm snippet might explain what I want to do:

#include <iostream>

class A {
public:
const char *s;
};

static A array[] = {
{ "a" },
{ "b" },
// I wish I could just define a label to a given element like this,
// but this syntax is already used for designated inits (at least in gcc):
// element_c:
{ "c" },
{ "d" },
{ "e" },
{ "f" },
{ "g" },
};

int main()
{
// Access element "c"
std::cout << array[2].s << std::endl;

// Now I wish I could write it this:
// std::cout << element_c.s << std::endl;

// Or maybe:
// std::cout << array::element_c.s << std::endl;

return 0;
}

Is there a way to do this in C++ (maybe some preprocessor tricks)?

Thank you.

PS:
This is my first post to usenet, so bear with me.
 
G

Guest

Hello,

I have a global static array of structs and want to access a given
element using an identifier. I don't want to use the element subscript,
because it will change if I insert elements before the element I
want to access. In assembler I would simply add a label in front
of the element, but this doesn't work in C++.

The following programm snippet might explain what I want to do:

#include <iostream>

class A {
public:
const char *s;
};

static A array[] = {
{ "a" },
{ "b" },
// I wish I could just define a label to a given element like this,
// but this syntax is already used for designated inits (at least in gcc):
// element_c:
{ "c" },
{ "d" },
{ "e" },
{ "f" },
{ "g" },
};

int main()
{
// Access element "c"
std::cout << array[2].s << std::endl;

// Now I wish I could write it this:
// std::cout << element_c.s << std::endl;

// Or maybe:
// std::cout << array::element_c.s << std::endl;

return 0;
}

Is there a way to do this in C++ (maybe some preprocessor tricks)?

There are probably some pre-processor trick you can use, but I suspect
that would be the wrong solution. Since you mentioned assembly I suspect
you have a background with that and might thus not be very familiar with
the C++ way of doing things. If you could explain a little bit about
what you are trying to solve I might be able to give you better advice.
 
G

Gianni Mariani

Paul said:
Hello,

I have a global static array of structs and want to access a given
element using an identifier. I don't want to use the element subscript,
because it will change if I insert elements before the element I
want to access. In assembler I would simply add a label in front
of the element, but this doesn't work in C++.

The following programm snippet might explain what I want to do:

#include <iostream>

class A {
public:
const char *s;
};

static A array[] = {
{ "a" },
{ "b" },
// I wish I could just define a label to a given element like this,
// but this syntax is already used for designated inits (at least in gcc):
// element_c:
{ "c" },
{ "d" },
{ "e" },
{ "f" },
{ "g" },
};

int main()
{
// Access element "c"
std::cout << array[2].s << std::endl;

// Now I wish I could write it this:
// std::cout << element_c.s << std::endl;

// Or maybe:
// std::cout << array::element_c.s << std::endl;

return 0;
}

Is there a way to do this in C++ (maybe some preprocessor tricks)?

One C++ way to do this is via references. References can be initialized
by looking up the array using a special function or statically if you
know the index. If the definition of the reference is static then more
than likely the compiler will look it up directly.

As for creating linker labels, you're outa luck. There is very little
chance you can do that portably. There is nothing stopping you however
doing what you want using assembler if your platform supports it.

The code below would solve your issue, and is probably all I would do
until I knew that this solution raises a siginificant problem.

#include <iostream>
#include <cstring>

class A {
public:
const char *s;
bool CompKey( const char * str )
{
return strcmp( str, s ) == 0;
}
};

extern A & element_a;
extern A & element_c;
extern A & element_g;

static A array[] = {
{ "a" },
{ "b" },
// I wish I could just define a label to a given element like this,
// but this syntax is already used for designated inits (at least in gcc):
// element_c:
{ "c" },
{ "d" },
{ "e" },
{ "f" },
{ "g" },
};

template <typename T, int N>
char (&EndOfHelper( T (&)[N] ))[N];
#define EndOf(A) ((A)+sizeof(EndOfHelper(A)))

A & findA( const char * istr )
{
for ( A * i = array; i < EndOf( array ); ++ i )
{
if ( i->CompKey( istr ) )
{
return *i;
}
}
throw "Failed to kind key in array";
}


A & element_a = array[ 0 ];
A & element_c = findA( "c" );
A & element_g = findA( "g" );

int main()
{
// Access element "c"
std::cout << array[2].s << std::endl;

// Now I wish I could write it this:
// std::cout << element_c.s << std::endl;

// Or maybe:
// std::cout << array::element_c.s << std::endl;

return 0;
}
 
P

Paul Brettschneider

Hello,

I have a global static array of structs and want to access a given
element using an identifier. I don't want to use the element subscript,
because it will change if I insert elements before the element I
want to access. In assembler I would simply add a label in front
of the element, but this doesn't work in C++.

[...]

Is there a way to do this in C++ (maybe some preprocessor tricks)?

There are probably some pre-processor trick you can use, but I suspect
that would be the wrong solution. Since you mentioned assembly I suspect
you have a background with that and might thus not be very familiar with
the C++ way of doing things. If you could explain a little bit about
what you are trying to solve I might be able to give you better advice.

I have, for example, a number of "Server-Objects" which are registered
when connecting to the server and can be used afterwards. When registering
or when generating statistics, it is convenient to loop over an array,
later I want to access them by name.

What I do right now is define global objects and then construct an array
of pointers:

static A element_a = { "a" };
static A element_b = { "b" };
static A element_c = { "c" };

static A *array[] = {
&element_a,
&element_b,
&element_c,
// ...
NULL // sentinel
};

Another possibility is to do it the other way round, like Gianni suggested:
have an array of objects and initialise references with a find()-function
at program-initialisation time.

I was hoping for a more convenient way.

BTW: I think accessing array-elements by name would sometimes be
convenient for local arrays too:

int func()
{
int array[] = {1, element2: 2, 3 };
return element2; // Alias for array[1]
}

Thanks.
 
B

BobR

Paul Brettschneider said:
Hello,
I have a global static array of structs and want to access a given
element using an identifier. I don't want to use the element subscript,
because it will change if I insert elements before the element I
want to access. In assembler I would simply add a label in front
of the element, but this doesn't work in C++.
The following programm snippet might explain what I want to do:

#include <iostream>

class A{ public:
const char *s;
};

static A array[] = { { "a" }, { "b" },
// I wish I could just define a label to a given element like this,
// but this syntax is already used for designated inits (at least in gcc):
// element_c:
{ "c" }, { "d" }, { "e" }, { "f" }, { "g" } };

int
// Access element "c"
std::cout << array[2].s << std::endl;
// Now I wish I could write it this:
// std::cout << element_c.s << std::endl;
// Or maybe:
// std::cout << array::element_c.s << std::endl;
return 0;
}

Is there a way to do this in C++ (maybe some preprocessor tricks)?
Thank you.
PS: This is my first post to usenet, so bear with me.

A std::map might be an option. Depends on how 'locked-in' you are in your
current design.

// - a loose example -
#include <iostream>
#include <string>
#include <map>

class PaulA{ public:
const char *s;
PaulA() : s(0){}
PaulA( char const *ss ) : s(ss){}
};

int main(){ using std::cout; // for NG post
std::map<std::string, PaulA> PaulMap;
PaulMap["a"] = PaulA("a");
PaulMap["b"] = PaulA("because");
PaulMap["c"] = PaulA("c");
std::string key( "d" );
PaulMap[key] = PaulA( key.c_str() );

cout<<" PaulMap[\"a\"] ="<<PaulMap["a"].s<<std::endl;
cout<<" PaulMap[\"b\"] ="<<PaulMap["b"].s<<std::endl;
cout<<" PaulMap[\"c\"] ="<<PaulMap["c"].s<<std::endl;
cout<<" PaulMap[key] ="<<PaulMap[key].s<<std::endl;

std::string keys( "efgh" );
for( size_t i(0); i < keys.size(); ++i ){
std::string tmp( 1, keys.at(i) ); // a bit on the 'ugly' side <G>
PaulMap[ tmp ] = PaulA( tmp.c_str() );
} // for(i)

for( size_t i(0); i < keys.size(); ++i ){
std::string tmp( 1, keys.at(i) );
cout<<" PaulMap["<<tmp<<"] ="
<<PaulMap[tmp].s<<std::endl;
} // for(i)

return 0;
} // main()

/* -output-
PaulMap["a"] =a
PaulMap["b"] =because
PaulMap["c"] =c
PaulMap[key] =d
PaulMap[e] =e
PaulMap[f] =f
PaulMap[g] =g
PaulMap[h] =h
*/
 
G

Guest

Hello,

I have a global static array of structs and want to access a given
element using an identifier. I don't want to use the element subscript,
because it will change if I insert elements before the element I
want to access. In assembler I would simply add a label in front
of the element, but this doesn't work in C++.

[...]

Is there a way to do this in C++ (maybe some preprocessor tricks)?

There are probably some pre-processor trick you can use, but I suspect
that would be the wrong solution. Since you mentioned assembly I suspect
you have a background with that and might thus not be very familiar with
the C++ way of doing things. If you could explain a little bit about
what you are trying to solve I might be able to give you better advice.

I have, for example, a number of "Server-Objects" which are registered
when connecting to the server and can be used afterwards. When registering
or when generating statistics, it is convenient to loop over an array,
later I want to access them by name.

What I do right now is define global objects and then construct an array
of pointers:

static A element_a = { "a" };
static A element_b = { "b" };
static A element_c = { "c" };

static A *array[] = {
&element_a,
&element_b,
&element_c,
// ...
NULL // sentinel
};

You could use std::map<std::string, A> servers. Then you could access a
specific one using servers["the_one"], and you can also loop over all of
them using iterators.
Another possibility is to do it the other way round, like Gianni suggested:
have an array of objects and initialise references with a find()-function
at program-initialisation time.

I was hoping for a more convenient way.

BTW: I think accessing array-elements by name would sometimes be
convenient for local arrays too:

int func()
{
int array[] = {1, element2: 2, 3 };
return element2; // Alias for array[1]
}

If it's only for a few elements a reference will do, if it's for many
elements perhaps they should not be in an array.
 
P

Paul Brettschneider

On Sun, 26 Aug 2007 17:24:03 +0000, BobR wrote:

Hello Bob,
Hello Erik,
Paul Brettschneider said:
Hello,
I have a global static array of structs and want to access a given
element using an identifier. I don't want to use the element subscript,
because it will change if I insert elements before the element I
want to access. In assembler I would simply add a label in front
of the element, but this doesn't work in C++.
[...]
Is there a way to do this in C++ (maybe some preprocessor tricks)?
Thank you.
PS: This is my first post to usenet, so bear with me.

A std::map might be an option. Depends on how 'locked-in' you are in your
current design.

// - a loose example -
#include <iostream>
#include <string>
#include <map>

class PaulA{ public:
const char *s;
PaulA() : s(0){}
PaulA( char const *ss ) : s(ss){}
};

int main(){ using std::cout; // for NG post
std::map<std::string, PaulA> PaulMap;
PaulMap["a"] = PaulA("a");
PaulMap["b"] = PaulA("because");
PaulMap["c"] = PaulA("c");
std::string key( "d" );
PaulMap[key] = PaulA( key.c_str() );

cout<<" PaulMap[\"a\"] ="<<PaulMap["a"].s<<std::endl;
cout<<" PaulMap[\"b\"] ="<<PaulMap["b"].s<<std::endl;
cout<<" PaulMap[\"c\"] ="<<PaulMap["c"].s<<std::endl;
cout<<" PaulMap[key] ="<<PaulMap[key].s<<std::endl;

std::string keys( "efgh" );
for( size_t i(0); i < keys.size(); ++i ){
std::string tmp( 1, keys.at(i) ); // a bit on the 'ugly' side <G>
PaulMap[ tmp ] = PaulA( tmp.c_str() );
} // for(i)

for( size_t i(0); i < keys.size(); ++i ){
std::string tmp( 1, keys.at(i) );
cout<<" PaulMap["<<tmp<<"] ="
<<PaulMap[tmp].s<<std::endl;
} // for(i)

return 0;
} // main()

/* -output-
PaulMap["a"] =a
PaulMap["b"] =because
PaulMap["c"] =c
PaulMap[key] =d
PaulMap[e] =e
PaulMap[f] =f
PaulMap[g] =g
PaulMap[h] =h
*/

Thanks for your ideas. I know it's a common beginner mistake to care about
performance issues that can't even be measured, but looking up an
associative array at runtime, when the association can be resolved at
compile/linktime or at programm start "feels" wrong. In this case the
lookup will be orders of magnitude faster than the operation on the
element, so it wouldn't matter.

Anyway, I think I will use your idea, but instead of string-ids I will be
using an enum and be populating an id-to-object lookup-table at program
start. Similar to this:

#include <iostream>
#include <algorithm>
#include <iterator>

class A {
public:
int id;
const char *s;
};

class Array {
public:
enum Id {
element_a = 0,
element_b,
element_c,
element_last // Must be last
};
private:
static A items[];
A *last;
A *id2A[element_last];
public:
Array();
A &operator[](Id id) { return *id2A[id]; }
A *begin() { return items; }
A *end() { return last; }

};

A Array::items[] = {
{ element_a, "a" },
{ element_c, "c" }, // Order needn't be the same
{ element_b, "b" }, // as in enum
{ element_last, NULL } // Must be last
};

Array::Array()
{
A *it;
for(it = items; it->id != element_last; ++it)
id2A[it->id] = it;
last = it;
}

std::eek:stream &operator<< (std::eek:stream &out, const A &a)
{
return out << a.s;
}

static Array array;

int main()
{
// Access all elements
std::copy(array.begin(),
array.end(),
std::eek:stream_iterator<A>(std::cout, "\n"));

// Access elements by id
std::cout << array[Array::element_a] << '\n'
<< array[Array::element_b] << '\n'
<< array[Array::element_c] << std::endl;

return 0;
}

It's too bad that C++ doesn't know C99-style designated initialisators.
IMHO it's a really nice feature which I could (mis)use like this:

// Untested - doesn't compile
#include <iostream>
#include <algorithm>
#include <iterator>

class A {
public:
enum ids {
element_a = 0,
element_b,
element_c,
element_last // Must be last!
};
const char *s;
};

static A array[] = {
[A::element_a] = { "a" }, // Doesn't compile!
[A::element_c] = { "c" },
[A::element_last] = { NULL } // Order irrelevant
[A::element_b] = { "b" },
};

std::eek:stream &operator<< (std::eek:stream &out, const A &a)
{
return out << a.s;
}

int main()
{
// Access all elements
std::copy(&array[A::element_a],
&array[A::element_last],
std::eek:stream_iterator<A>(std::cout, "\n"));

// Access all elements by id
std::cout << &array[A::element_a] << '\n'
<< &array[A::element_b] << '\n'
<< &array[A::element_c] << std::endl;

return 0;
}

Does anybody know if this is planned in a future C++-standard?
 
G

Guest

On Sun, 26 Aug 2007 17:24:03 +0000, BobR wrote:

Hello Bob,
Hello Erik,
Paul Brettschneider said:
Hello,
I have a global static array of structs and want to access a given
element using an identifier. I don't want to use the element subscript,
because it will change if I insert elements before the element I
want to access. In assembler I would simply add a label in front
of the element, but this doesn't work in C++.
[...]
Is there a way to do this in C++ (maybe some preprocessor tricks)?
Thank you.
PS: This is my first post to usenet, so bear with me.

A std::map might be an option. Depends on how 'locked-in' you are in your
current design.

// - a loose example -
#include <iostream>
#include <string>
#include <map>

class PaulA{ public:
const char *s;
PaulA() : s(0){}
PaulA( char const *ss ) : s(ss){}
};

int main(){ using std::cout; // for NG post
std::map<std::string, PaulA> PaulMap;
PaulMap["a"] = PaulA("a");
PaulMap["b"] = PaulA("because");
PaulMap["c"] = PaulA("c");
std::string key( "d" );
PaulMap[key] = PaulA( key.c_str() );

cout<<" PaulMap[\"a\"] ="<<PaulMap["a"].s<<std::endl;
cout<<" PaulMap[\"b\"] ="<<PaulMap["b"].s<<std::endl;
cout<<" PaulMap[\"c\"] ="<<PaulMap["c"].s<<std::endl;
cout<<" PaulMap[key] ="<<PaulMap[key].s<<std::endl;

std::string keys( "efgh" );
for( size_t i(0); i < keys.size(); ++i ){
std::string tmp( 1, keys.at(i) ); // a bit on the 'ugly' side <G>
PaulMap[ tmp ] = PaulA( tmp.c_str() );
} // for(i)

for( size_t i(0); i < keys.size(); ++i ){
std::string tmp( 1, keys.at(i) );
cout<<" PaulMap["<<tmp<<"] ="
<<PaulMap[tmp].s<<std::endl;
} // for(i)

return 0;
} // main()

/* -output-
PaulMap["a"] =a
PaulMap["b"] =because
PaulMap["c"] =c
PaulMap[key] =d
PaulMap[e] =e
PaulMap[f] =f
PaulMap[g] =g
PaulMap[h] =h
*/

Thanks for your ideas. I know it's a common beginner mistake to care about
performance issues that can't even be measured, but looking up an
associative array at runtime, when the association can be resolved at
compile/linktime or at programm start "feels" wrong. In this case the
lookup will be orders of magnitude faster than the operation on the
element, so it wouldn't matter.

Anyway, I think I will use your idea, but instead of string-ids I will be
using an enum and be populating an id-to-object lookup-table at program
start. Similar to this:

#include <iostream>
#include <algorithm>
#include <iterator>

class A {
public:
int id;
const char *s;
};

class Array {
public:
enum Id {
element_a = 0,
element_b,
element_c,
element_last // Must be last
};
private:
static A items[];
A *last;
A *id2A[element_last];
public:
Array();
A &operator[](Id id) { return *id2A[id]; }
A *begin() { return items; }
A *end() { return last; }

};

A Array::items[] = {
{ element_a, "a" },
{ element_c, "c" }, // Order needn't be the same
{ element_b, "b" }, // as in enum
{ element_last, NULL } // Must be last
};

Array::Array()
{
A *it;
for(it = items; it->id != element_last; ++it)
id2A[it->id] = it;
last = it;
}

std::eek:stream &operator<< (std::eek:stream &out, const A &a)
{
return out << a.s;
}

static Array array;

int main()
{
// Access all elements
std::copy(array.begin(),
array.end(),
std::eek:stream_iterator<A>(std::cout, "\n"));

// Access elements by id
std::cout << array[Array::element_a] << '\n'
<< array[Array::element_b] << '\n'
<< array[Array::element_c] << std::endl;

return 0;
}

It's too bad that C++ doesn't know C99-style designated initialisators.
IMHO it's a really nice feature which I could (mis)use like this:

// Untested - doesn't compile
#include <iostream>
#include <algorithm>
#include <iterator>

class A {
public:
enum ids {
element_a = 0,
element_b,
element_c,
element_last // Must be last!
};
const char *s;
};

static A array[] = {
[A::element_a] = { "a" }, // Doesn't compile!
[A::element_c] = { "c" },
[A::element_last] = { NULL } // Order irrelevant
[A::element_b] = { "b" },
};

std::eek:stream &operator<< (std::eek:stream &out, const A &a)
{
return out << a.s;
}

int main()
{
// Access all elements
std::copy(&array[A::element_a],
&array[A::element_last],
std::eek:stream_iterator<A>(std::cout, "\n"));

// Access all elements by id
std::cout << &array[A::element_a] << '\n'
<< &array[A::element_b] << '\n'
<< &array[A::element_c] << std::endl;

return 0;
}

Does anybody know if this is planned in a future C++-standard?

While they might not work quite like the initialisers from C99 the next
C++ standard will contain something called initialiser lists, which are
a bit more flexible in the sense that they can be used on user-defined
types as well.
 
B

BobR

Paul Brettschneider said:
Thanks for your ideas. I know it's a common beginner mistake to care about
performance issues that can't even be measured, but looking up an
associative array at runtime, when the association can be resolved at
compile/linktime or at programm start "feels" wrong. In this case the
lookup will be orders of magnitude faster than the operation on the
element, so it wouldn't matter.

Anyway, I think I will use your idea, but instead of string-ids I will be
using an enum and be populating an id-to-object lookup-table at program
start. Similar to this:

#include <iostream>
#include <algorithm>
#include <iterator>

class A { public: int id; const char *s; };

class Array { public:
enum Id { element_a = 0, element_b, element_c,
element_last // Must be last
};
private:
static A items[];
A *last;
A *id2A[element_last];
public:
Array();
A &operator[](Id id) { return *id2A[id]; }
A *begin() { return items; }
A *end() { return last; }
};

A Array::items[] = {
{ element_a, "a" },
{ element_c, "c" }, // Order needn't be the same
{ element_b, "b" }, // as in enum
{ element_last, NULL } // Must be last
};

Array::Array(){
A *it;
for(it = items; it->id != element_last; ++it) id2A[it->id] = it;
last = it;
}

std::eek:stream &operator<< (std::eek:stream &out, const A &a){
return out << a.s;
}

static Array array;

int main(){
// Access all elements
std::copy(array.begin(),
array.end(),
std::eek:stream_iterator<A>(std::cout, "\n"));
// Access elements by id
std::cout << array[Array::element_a] << '\n'
<< array[Array::element_b] << '\n'
<< array[Array::element_c] << std::endl;
return 0;
}

Now it looks (to me) like you are re-inventing the wheel.
The 'wheel' in this case is std::vector.

// includes <iostream>, <vector>, <algorithm>
int main(){
enum Id{ element_a = 0, element_b, element_c,
element_last // Must be last
}; // Id

std::vector<std::string> Array( element_last );

Array.at( element_a ) = "a";
Array.at( element_b ) = "b";
Array.at( element_c ) = "c";
// Array.at( element_last ) = ""; // error. not needed, it's default.

std::copy( Array.begin(), Array.end(),
std::eek:stream_iterator<std::string>(std::cout, "\n") );
// - Access elements by id -
std::cout << Array.at( element_a ) << '\n'
<< Array.at( element_b ) << '\n'
<< Array.at( element_c ) << std::endl;
if ( Array.at( element_b ) != Array.at( element_c ) ){
std::swap( Array.at( element_b ), Array.at( element_c ) );
} // if(b!=c)
std::copy( Array.begin(), Array.end(),
std::eek:stream_iterator<std::string>(std::cout, "\n") );
return 0;
} // main()

/* - output: Paul vectors -
a
b
c
a // - Access elements by id -
b
c
a
c
b
*/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,833
Latest member
BettyeMacf

Latest Threads

Top