Accessing the attributes of a struct in C++ as array elements?
In C++, it might be reasonable to represent a URL using a class or a struct made of several strings, like so:
struct basic {
std::string protocol;
std::string username;
std::string password;
std::string hostname;
std::string port;
std::string pathname;
std::string search;
std::string hash;
};
To associate each component with an index, we can use an enum class:
enum class component {
PROTOCOL = 0,
USERNAME = 1,
PASSWORD = 2,
HOSTNAME = 3,
PORT = 4,
PATHNAME = 5,
SEARCH = 6,
HASH = 7,
};
Initially, you might use a switch statement to access components:
std::string& get_component(basic& url, component comp) {
switch (comp) {
case component::PROTOCOL: return url.protocol;
case component::USERNAME: return url.username;
case component::PASSWORD: return url.password;
case component::HOSTNAME: return url.hostname;
case component::PORT: return url.port;
case component::PATHNAME: return url.pathname;
case component::SEARCH: return url.search;
case component::HASH: return url.hash;
}
}
The switch case might introduce performance overhead if accessed frequently.
For better performance, you could store the components in an array:
struct fat {
std::array<std::string, 8> data;
std::string &protocol = data[0];
std::string &username = data[1];
std::string &password = data[2];
std::string &hostname = data[3];
std::string &port = data[4];
std::string &pathname = data[5];
std::string &search = data[6];
std::string &hash = data[7];
};
Alternatively, you could avoid hardcoding the indexes:
struct fat {
std::array<std::string, 8> data;
std::string& protocol = data[int(Component::PROTOCOL)];
std::string& username = data[int(Component::USERNAME)];
std::string& password = data[int(Component::PASSWORD)];
std::string& hostname = data[int(Component::HOSTNAME)];
std::string& port = data[int(Component::PORT)];
std::string& pathname = data[int(Component::PATHNAME)];
std::string& search = data[int(Component::SEARCH)];
std::string& hash = data[int(Component::HASH)];
};
With this new data structure, getting a component by its index becomes simpler:
std::string& get_component(fat& url, component comp) {
return url.data[int(comp)];
};
Unfortunately, each reference in the new fat data structure might use 8 bytes on a 64-bit system. That is not a concern if you expect to have few instances of the data structures. However, if you do repeated create instances, you might want to avoid the references. You might try to replace the references by simple methods:
struct advanced {
std::array<std::string, 8> data;
std::string &protocol() { return data[0]; }
std::string &username() { return data[1]; }
std::string &password() { return data[2]; }
std::string &hostname() { return data[3]; }
std::string &port() { return data[4]; }
std::string &pathname() { return data[5]; }
std::string &search() { return data[6]; }
std::string &hash() { return data[7]; }
};
Or, if you prefer, you can be more explicit:
struct fat_advanced {
std::array<std::string, 8> data;
std::string& protocol() { return data[int(Component::PROTOCOL)]; }
std::string& username() { return data[int(Component::USERNAME)]; }
std::string& password() { return data[int(Component::PASSWORD)]; }
std::string& hostname() { return data[int(Component::HOSTNAME)]; }
std::string& port() { return data[int(Component::PORT)]; }
std::string& pathname() { return data[int(Component::PATHNAME)]; }
std::string& search() { return data[int(Component::SEARCH)]; }
std::string& hash() { return data[int(Component::HASH)]; }
};
It is not entirely satisfactory as it requires calling methods instead of accessing attributes. Eugene Ostroukhov has a better approach using pointer-to-member operator. We create a compile-time array with pointers to the attribute of the ‘basic’ struct:
std::string& get_component_fast(basic& url, component comp) {
constexpr static std::array accessors = {&basic::protocol,
&basic::username, &basic::password, &basic::hostname,
&basic::port, &basic::port, &basic::pathname,
&basic::search, &basic::hash};
return url.*accessors[int(comp)];
}
In this manner, you do not need to modify the original class, and you are likely to get excellent performance.
MSc Physics | Data Scientist | Machine Learning | Software Engineer
8moIs she checking for buffer overflows or if the struct fits in the cache ?
Director of Engineering at Dataiku
8moSome introspection capability is missing to C++ for so long...
Post-Doctoral Researcher a ÉTS - École de technologie supérieure
8moIt is always a pleasure to read your pedagogical posts full of technical wisdom.