When handling XML attributes in C++, how should different operations be run for different attributes?
Currently, I have something like this:
// get list of attributes for an XML element into member called 'attributes'
// ...
// run appropriate functions for each attribute
for (auto attribute : attributes)
{
auto name = attribute.name;
auto value = attribute.value;
if (name == "x")
doSomethingWithX(value);
else if (name == "y")
doSomethingWithY(value);
}
For just a few attribute names, this isn't so bad - but with a larger number (>15) this starts to look messy and I'm concerned about performance issues.
What might be a better way of handling XML attributes like this?
You can use a std::unordererd_map<std::string, std::function<void (const std::string&)>> and set it up with appropriate lambda functions:
std::unordererd_map<std::string, std::function<void (const std::string&)>> attrProcessors = {
{ "X", [](const std::string& value) {
// Do something with value
} } } ,
{ "Y", [](const std::string& value) {
// Do something with value
} } }
};
// run appropriate functions for each attribute
for (auto attribute : attributes)
{
auto name = attribute.name;
auto value = attribute.value;
auto processorEntry = attrProcessors.find(name);
if(processorEntry != attrProcessors.end()) {
(*processorEntry).second(value);
}
}
I am not so sure though that maintenace of the map entries would be easier to read than the if / else if cascade.
On the other hand you won't need to create an extra function for each attribute name.
Related
I want to create a function that takes a string that might be :
"triangle" , "square", or "rectangle"
And according to this argument, I want to return a pointer on a class Form.
I have a mother class "Form", who inherits of "Class Rectangle", "Class Square", and "Class Rectangle"
But I don't want to do :
if (name == "rectangle")
return (new Rectangle());
else if (name == "square")
return (new Square());
... etc
I thought about pointers on functions, but I wanted the simplest method and clean code, what do you recommand ?
Thank's !
Yes, you could use function pointers or lambdas. You can use a map of strings to functors:
std::map<std::string, std::function<ShapeBase*()>> actions = {
{ "rectangle", []{return new Rectangle;} },
{ "square", []{return new Square;} }
};
return actions[name]();
But if you're not going to change the actions at run time, it's hard to beat what you had really.
In a comment you asked "Imagine it had 500 forms". Indeed, the map lookup will be faster than 500 chained if statements. You could make it a switch with some effort: use hashes for the names instead of the strings themselves. If the hash function is constexpr you can write that easily:
switch (hash(name)) {
case hash("rectangle"): return new Rectangle;
case hash("square"): return new Square;
// 500 cases
}
The switch statement will be optimized to do a binary search or something like that, on the integer hash values. You'll also get a compile-time error if there is a hash clash.
You can use std::unordered_map:
using FormPtr = std::unique_ptr<Form>;
using Creators = std::unordered_map<std::string,std::function<FormPtr()>>;
FormPtr create( const std::string &name )
{
const static Creators creators {
{ "triangle", [] { return std::make_unique<Triangle>(); } },
{ "square", [] { return std::make_unique<Square>(); } },
{ "rectangle", [] { return std::make_unique<Rectangle>(); } }
};
auto f = creators.find( name );
if( f == creators.end() ) {
// error handling here
}
return f->second();
}
If you need to add creators outside you can put them into a class and allow them to update the map and register more creators dynamically.
Trying to create JSONs on the fly for my application, but the JSON I recieve does not remain constant.
std::string ResponseJson::getValue(std::string filter, std::string filterName, std::string jsonIndex)
{
BOOST_FOREACH(boost::property_tree::ptree::value_type & arrayElement, root.get_child("message"))
{
std::string value = arrayElement.second.get<std::string>(filter);
if (value == filterName)
{
return arrayElement.second.get<std::string>(jsonIndex);
}
}
return "";
}
above code snippet works well but message is the child which can be changed in different jsons so how can I make this function generic ?
I'm currently creating a custom std::unordered_map declaration with my custom key:
class BASE_DLLSPEC ClientKey
{
private:
// this is always true initially until we call SetClientId
bool emptyId;
// both of these are guaranteed to be unique
QString m_connectId; // ip:port format
QString m_clientId; // {Uuid} format
// ----------
public:
ClientKey(const QString& connectId = "", const QString& clientId = "") :
emptyId(true), m_connectId(connectId), m_clientId(clientId)
{ }
void SetClientId(const QString& clientId)
{
m_clientId = clientId;
emptyId = false;
}
const QString& GetConnectId() const { return m_connectId; }
const QString& GetClientId() const { return m_clientId; }
bool operator==(const ClientKey& other) const
{
int comp1 = QString::compare(m_connectId, other.GetConnectId());
int comp2 = QString::compare(m_clientId, other.GetClientId());
return (comp1 == 0) ||
(!emptyId && comp2 == 0);
}
};
struct BASE_DLLSPEC ClientKeyHash
{
std::size_t operator()(const ClientKey& key) const
{
std::string connectId = key.GetConnectId().toStdString();
std::string clientId = key.GetClientId().toStdString();
std::size_t h1 = std::hash<std::string>()(connectId);
std::size_t h2 = std::hash<std::string>()(clientId);
return h1 ^ (h2 << 1);
}
};
struct BASE_DLLSPEC ClientKeyEqual
{
bool operator()(const ClientKey& lhs, const ClientKey& rhs) const
{
return lhs == rhs;
}
};
typedef std::unordered_map<ClientKey,
ClientPtr,
ClientKeyHash,
ClientKeyEqual> ClientMap;
I'm having difficulties finding a particular key during my iteration. For some reason my client object is never located when I pass in a key for lookup.
ClientKey key = Manager::ClientKey(connectId);
ClientManager& clientManager = Manager::ClientManager::GetInstance();
ClientMap::const_iterator clientIter = clientManager.GetClients().find(key);
Even if the key has already been inserted, clientIter is always pointing to the end iterator position. Do you think this is related to having to re-create these ClientKey values on the stack and then passing them into the map for look-up, or do I have a problem elsewhere? Thank you for the clarification and insight.
At first, some considerations to the emptyId field (do not consider invalid formats - which, by the way, is not checked by you either):
ClientKey k0("hello", "world");
ClientKey k1("hello");
k1.SetClientId("world");
Is there any particular reason that the emtpyId flag should be different for k0 and k1? I personally would say:
The flag is implemented incorrectly.
It is redundant, you get the same information via m_clientId.empty().
Now the reason for failure:
Consider again k0 and k1, but without SetClientId having been called on k1:
ClientKey k0("hello", "world");
ClientKey k1("hello");
Imagine k0 has been inserted in the map, and with k1 you try to find it. What will happen? k1 produces another hash key than k0, and the map will look at a different bucket than where k0 resides at - and will not find anything.
What I think you want to achieve is having several clients for the same connection id and being able to iterate over these for a given connection id. So you might prefer std::unordered_multimap<std::string, ClientPtr> (where the string parameter represents the connection id). You will get all clients for a given connection id via equal_range then, and your class ClientKey gets obsolete.
Your code allows that the following will return true:
ClientKey k1("hello", "world");
ClientKey k2("hello", "");
return k1 == k2;
However, your hash is based on the combination of connectId and clientId.
unordered_map::find does not do an exhaustive search of the map, instead it looks in the bucket for the given hash and compares just the entries in the bucket.
You are generating your test key with just connectId, so it is looking in the bucket for ClientKey(connectId, "") rather than the bucket for ClientKey(connectId, someOtherValue).
You should consider making the hash based exclusively on connectId.
Lastly, note your constructor:
ClientKey(const QString& connectId = "", const QString& clientId = "") :
emptyId(true), m_connectId(connectId), m_clientId(clientId)
{ }
If I write:
ClientKey ck("hello");
should emptyId really be true?
For my assignment, I'm storing user login infos. I'm taking in a string which is the command. The command can be create, login, remove, etc. There are 10 total options, i.e 10 different strings possible. Can anyone explain a more efficient way to write this instead of 10 if and else if statements? Basically how should I format/structure things besides using a bunch of if (string == "one"), else if (string == "two"). Thank you
I expect that your lecturer would like you to extract function to another re-usable function:
string action;
command = CreateAction(action);
command.Do(...);
Ofcourse, inside you CreateAction class you still need to have the conditionals that determine which commands need to be created.
AbstractCommand CreateAction(action)
{
if (action == "login")
return LoginCommand();
else if (action == "remove")
return RemoveCommand();
..... etc etc
}
And if you really want to get rid of all the conditionals than you can create some self-registering commands but that involves a lot more code and classes......
You should look up things like Command Pattern and Factory Pattern
You can use function pointers and a lookup table.
typedef void (*Function_Pointer)(void);
void Create(void);
void Login(void);
void Remove(void);
struct Function_Option_Entry
{
const char * option_text;
Function_Pointer p_function;
};
Function_Option_Entry option_table[] =
{
{"one", Create},
{"two", Login},
{"three", Remove},
};
const unsigned int option_table_size =
sizeof(option_table) / sizeof(option_table[0]);
//...
std::string option_text;
//...
for (i = 0; i < option_table_size; ++i)
{
if (option_text == option_table[i].option_text)
{
option_table[i].p_function();
break;
}
}
Use a switch, and a simple hash-function.
You need to use a hash-function, because C and C++ only allow switching on integral values.
template<size_t N> constexpr char myhash(const char &x[N]) { return x[0] ^ (x[1]+63); }
char myhash(const string& x) { return x.size() ? x[0] ^ (x[1]+63) : 0; }
switch(myhash(s)) {
case myhash("one"):
if(s != "one") goto nomatch;
// do things
break;
case myhash("two"):
if(s != "two") goto nomatch;
// do things
break;
default:
nomatch:
// No match
}
Slight adjustments are needed if you are not using std::string.
I would recommend you to create a function for every specific string. For example, if you receive a string "create" you will call function doCreate(), if you receive a string "login" then you call function doLogin()
The only restriction on these function is that all of them must have the same signature. In an example above it was smh like this:
typedef void (*func_t) ();
The idea is to create a std::map from strings to these functions. So you wouldn't have to write 10 if's or so because you will be able to simple choose the right function from the map by the name of a specific string name. Let me explain it by the means of a small example:
typedef void (*func_t) ();
void doCreate()
{
std::cout << "Create function called!\n";
}
void doLogin()
{
std::cout << "Login function called!\n";
}
std::map<std::string, func_t> functionMap;
void initMap()
{
functionMap["create"] = doCreate;
functionMap["login"] = doLogin;
}
int main()
{
initMap();
std::string str = "login";
functionMap[str](); // will call doLogin()
str = "create";
functionMap[str](); // will call doCreate()
std::string userStr;
// let's now assume that we also can receive a string not from our set of functions
std::cin >> userStr;
if (functionMap.count(userStr))
{
functionMap[str](); // now we call doCreate() or doLogin()
}
else
{
std::cout << "Unknown command\n";
}
return 0;
}
I hope it will help you in someway=)
You can use a map which does the comparison for you.
Something like this:
Initialise map:
std::map<std::string, std::function<void(std::string&)>> map;
map["login"] = std::bind(&Class::DoLogin, this, std::placeholders::_1);
map["create"] = std::bind(&Class::DoCreate, this, std::placeholders::_1);
Receive message:
map.at(rx.msg_type)(rx.msg_data);
Handler:
void Class::DoLogin(const std::string& data)
{
// do login
}
Maybe you can create a std::map<std::string, int> and use map lookups to get the code of the command that was passed - you can later switch on that number. Or create an enum Command and have a std::map<std::string, Command> and use the switch.
Example:
enum Command
{
CREATE,
LOGIN,
...
};
std::map<std::string, Command> commandNameToCode;
// fill the map with appropriate values
commandNameToCode["create"] = Command::CREATE;
// somehow get command name from user and store in the below variable (not shown)
std::string input;
// check if the command is in the map and if so, act accordingly
if(commandNameToCode.find(input) != commandNameToCode.end())
{
switch(commandNameToCode[input])
{
case CREATE:
// handle create
break;
...
}
}
I am using jsoncpp to read settings from a JSON file.
I would like to have two cascading settings file, say MasterSettings.json and LocalSettings.json where LocalSettings is a subset of MasterSettings. I would like to load MasterSettings first and then LocalSettings. Where LocalSettings has a value that differs from MasterSettings, that value would overwrite the one from MasterSettings. Much like the cascade in CSS.
Is there any elegant way to do this with jsoncpp?
I'm going to assume your settings files are JSON objects.
As seen here, when JSONCpp parses a file, it clears the contents of the root node. This mean that trying to parse a new file on top of the old one won't preserve the old data. However, if you parse both files into separate Json::Value nodes, it's straight forward to recursively copy the values yourself by iterating over the keys in the second object using getMemberNames.
// Recursively copy the values of b into a. Both a and b must be objects.
void update(Json::Value& a, Json::Value& b) {
if (!a.isObject() || !b.isObject()) return;
for (const auto& key : b.getMemberNames()) {
if (a[key].isObject()) {
update(a[key], b[key]);
} else {
a[key] = b[key];
}
}
}
I know it has been a while. but...
In addition to the correct answer and the commentary, here is a code version for those who use a older g++ version:
void jsonMerge(Json::Value &a, Json::Value &b) {
if (!a.isObject() || !b.isObject()) return;
vector<string> member_name = b.getMemberNames();
string key = "";
for (unsigned i = 0, len = member_name.size(); i < len; i++) {
key = member_name[i];
if (!a[key].isNull() && a[key].type() == Json::objectValue && b[key].type() == Json::objectValue) {
jsonMerge(a[key], b[key]);
} else {
a[key] = b[key];
}
}
member_name.clear();
}