On Sun, 26 Aug 2007 17:24:03 +0000, BobR wrote:
Hello Bob,
Hello Erik,
Paul Brettschneider <paul.brettschneider@yahoo.fr> wrote in message...
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::ostream &operator<< (std::ostream &out, const A &a)
{
return out << a.s;
}
static Array array;
int main()
{
// Access all elements
std::copy(array.begin(),
array.end(),
std::ostream_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::ostream &operator<< (std::ostream &out, const A &a)
{
return out << a.s;
}
int main()
{
// Access all elements
std::copy(&array[A::element_a],
&array[A::element_last],
std::ostream_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?
types as well.