I'm trying to create a map with a custom key, which is an object's pointer address, as mentioned.
I need the address because for now it's the only relevant way to compare between two objects.
from what i understood, the proper way of doing this is by using const char* as key
here is the typedef :
typedef __gnu_cxx::unordered_map<const char*, std::string> TargetsTags;
I'm a bit confused about the following:
how do I create the operator() ?
This is what i used for std::string:
namespace __gnu_cxx {
template<>
struct hash<std::string>
{
hash<const char*> h;
size_t operator()(const std::string &s) const
{
return h(s.c_str());
};
};
}
What about const char*?
And is this the correct way of doing this?
The working example using c++11:
#include <iostream>
#include <unordered_map>
#include <string>
#include <functional>
using namespace std;
class myhash {
public:
size_t operator() (const char *val) const {
return std::hash<std::string>()(val);
}
};
class myequal {
public:
bool operator()(const char *val1, const char *val2) const{
return std::string(val1) == std::string(val2);
}
};
int main() {
std::unordered_map<const char*, string, myhash, myequal> mymap;
mymap["abc"] = "abcd";
mymap["cba"] = "dcba";
std::cout << mymap["abc"] << std::endl;
return 0;
}
Related
check out code below, i overload hash function and string-equal function for nocase-c-string key(const char*), but the result is not what i expected: the main function will print not found, how to make it matched?
struct hash_c_string
{
std::size_t operator()(const char *ctx) const
{
return MurmurHash2(ctx, strlen(ctx),static_cast<size_t>(0xc70f6907UL));
}
};
typedef struct {
const char * name;
}PortMapConfig;
struct by_name{};
struct CompareEqual
{
inline bool operator()(const char* left, const char* right) const
{
return strcasecmp(left, right)==0;
}
};
using namespace boost::multi_index;
typedef
boost::multi_index_container<
PortMapConfig,
indexed_by<
hashed_unique<tag<by_name>, member<PortMapConfig, const char *, &PortMapConfig::name>, hash_c_string, CompareEqual>
>
> StuContainer;
int main() {
StuContainer con;
PortMapConfig st1 = {"Uplink0"};
con.insert(st1);
auto &indexofName = con.get<by_name>();
const char * s = "uplink0";
auto itr = indexofName.find(s);
if(itr != indexofName.end()){
std::cout << "name:"<<itr->name << std::endl;
}
else
std::cout << "not found!!!"<< std::endl;
return 0;
}
You're attempting to hash by a derived property, namely a case-folded name.
This is going to be expensive even if done right¹. All the signals here indicate that you might be doing these in the name of performance. If you need the case-insensitive lookup, I suggest to reify a key to index on, so you can have natural hash and equality on them.
¹ Importantly, as the commenter already points out, your solution has Undefined Behaviour because the hash/equality functions don't agree
You might even pre-fold the lookup key to avoid repeat efforts.
In this example I'd strongly suggest using string views, considering not hashing at all (have you profiled it with your load factors?).
Making your sample work, and also cleaned up a little:
Live On Coliru
#include <boost/locale.hpp>
#include <boost/locale/conversion.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index_container.hpp>
#include <iostream>
namespace bmi = boost::multi_index;
static auto inline fold_case(std::string_view sv) {
return boost::locale::fold_case(sv.data());
}
struct PortMapConfig {
/*explicit*/ PortMapConfig(std::string_view name): name(name) {}
std::string_view name;
std::string name_key = fold_case(name);
struct Hash {
std::size_t operator()(std::string_view sv) const {
return boost::hash_value(sv); // TODO(sehe): murmur instead
}
};
using Equal = std::equal_to<std::string_view>;
};
using StuContainer = bmi::multi_index_container<
PortMapConfig,
bmi::indexed_by<bmi::hashed_unique<
bmi::tag<struct by_name>,
bmi::member<PortMapConfig, std::string, &PortMapConfig::name_key>,
PortMapConfig::Hash,
PortMapConfig::Equal>
>>;
int main() {
std::locale::global(boost::locale::generator()("en_US.UTF-8"));
StuContainer con { {"Uplink0"}, {"Uplink1"} };
if (auto itr = con.find(fold_case("uplink1")); itr != con.end()) {
std::cout << "name:" << itr->name << " (" << itr->name_key << ")\n";
}
}
Prints
name:Uplink1 (uplink1)
I am currently trying to generate JSON with ordered keys and therefore used a workaround method. However, if I try to use it within my base and derived classes, I get an error which I do not really understand. It seems like it fails to call the to_Json methods (because the error appears if I try to map a DerivedClass-instance (test and test2) to my_json.
I have already tried the example without ordered keys (just by using json = nlohmann::json;) and it works completely fine. The keys in the output are sorted alphabetically and looks like this:
{
"cbor": "cbortest",
"diagnostic": "diagnose: corona",
"header": {
"headerId": 3,
"timestamp": "2019-12-10T16:04:00.00Z",
"version": "4.0.0"
},
"hex": "00f2",
"roundtrip": true
}
What I am trying to achieve through using the nlohmann fifo_map is to keep the insertion order and the final output therefore should look like this:
{
"header": {
"headerId": 3,
"timestamp": "2019-12-10T16:04:00.00Z",
"version": "4.0.0"
},
"cbor": "cbortest",
"hex": "00f2",
"roundtrip": true,
"diagnostic": "diagnose: corona"
}
Executing the following code outputs two errors:
Error C2440: 'initializing': cannot convert from 'BaseNamespace::SubNamespace::DerivedClass' to 'nlohmann::basic_json<my_workaround_fifo_map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>' ; in file: main.cpp
Please have a look at the following code:
In BaseClass.h:
#ifndef BASECLASS_H
#define BASECLASS_H
#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include "fifo_map.hpp"
namespace BaseNamespace{
namespace SubNamespace{
class BaseClass {
public:
BaseClass () {};
virtual ~BaseClass () {};
uint32_t getHeaderId() const { return headerId; };
std::string getTimestamp() const { return timestamp; };
std::string getVersion() const { return version; };
void setHeaderId(uint32_t str) { headerId = str; };
void setTimestamp(std::string str) { timestamp = str; };
void setVersion(std::string bo) { version = bo; };
void setHeader(UAgvHeader const& header) {
setHeaderId(header.getHeaderId());
setTimestamp(header.getTimestamp());
setVersion(header.getVersion());
}
private:
uint32_t headerId;
std::string timestamp;
std::string version;
};
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
using namespace nlohmann;
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;
void to_json(my_json &j, const BaseClass &p)
{
j = my_json{
{ "headerId", p.getHeaderId() },
{ "timestamp", p.getTimestamp() },
{ "version", p.getVersion() }
};
}
void from_json(const my_json &j, BaseClass &p)
{
p.setHeaderId(j.at("headerId").get< std::uint32_t>());
p.setTimestamp(j.at("timestamp").get< std::string >());
p.setVersion(j.at("version").get<std::string>());
}
} // namespace SubNamespace
} // namespace BaseNamespace
#endif // BASECLASS_H_
In DerivedClass.h:
#ifndef DERIVEDCLASS_H
#define DERIVEDCLASS_H
#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include <optional>
#include "BaseClass.h"
namespace BaseNamespace{
namespace SubNamespace{
class DerivedClass : public BaseClass {
public:
std::string getCBor() const { return cbor; };
std::string getHex() const { return hex; };
bool getRoundtrip() const { return roundtrip; };
std::optional<std::string> getDiagnostic() const { return diagnostic; };
void setCBor(std::string str) { cbor = str; };
void setHex(std::string str) { hex = str; };
void setRoundtrip(bool bo) { roundtrip = bo; };
void setDiagnostic(std::optional<std::string> opt_str) { diagnostic = opt_str; };
private:
std::string cbor;
std::string hex;
bool roundtrip;
std::optional<std::string> diagnostic = std::nullopt;
};
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
using namespace nlohmann;
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;
void to_json(my_json &j, const DerivedClass& p)
{
j["header"] = static_cast<BaseClass>(p);
j["cbor"] = p.getCBor();
j["hex"] = p.getHex();
j["roundtrip"] = p.getRoundtrip();
// assuming you only want a "diagnostic" key if there is an actual value;
// if not, store a nullptr and adjust the from_json accordingly
if (p.getDiagnostic() != std::nullopt)
{
j["diagnostic"] = p.getDiagnostic().value();
}
}
void from_json(const my_json &j, DerivedClass&p)
{
p.setHeader(j.at("header").get<BaseClass>());
p.setCBor(j.at("cbor").get< std::string >());
p.setHex(j.at("hex").get< std::string >());
p.setRoundtrip(j.at("roundtrip").get< bool >());
// if we also allow "null" values, then we need to add an "is_string()"
// check
if (j.count("diagnostic") != 0)
{
p.setDiagnostic(j.at("diagnostic").get< std::string >());
}
}
} // namespace SubNamespace
} // namespace BaseNamespace
#endif // DERIVEDCLASS_H
In main.cpp:
#include <iostream>
#include <string>
#include <nlohmann/json.hpp>
#include <iomanip>
#include <optional>
#include "DerivedClass.h"
using namespace nlohmann;
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;
int main(int argc, char* argv[]) {
BaseNamespace::SubNamespace::DerivedClass test;
test.setHeaderId(3);
test.setTimestamp("2019-12-10T16:04:00.00Z");
test.setVersion("4.0.0");
test.setCBor("cbortest");
test.setHex("00f2");
test.setRoundtrip(true);
test.setDiagnostic("diagnose: corona");
my_json j = test; // ERROR: no suitable conversion
std::cout << std::setw(2) << j << std::endl;
std::string str = R"({"header":
{ "headerId" : 4711,
"timestamp" : "1 Uhr",
"version" : "5.0.0"
},
"cbor" : "+X4A",
"hex" : "f97e00",
"roundtrip" : true,
"diagnostic" : "NaN"
})";
my_json j2 = my_json::parse(str);
BaseNamespace::SubNamespace::DerivedClass test2 = j2;
my_json k = test2; // ERROR: no suitable conversion
std::cout << std::setw(2) << k << std::endl;
return 0;
}
Is it possible to write const function with apply_visitor inside?
For example, this code compiles without errors:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <boost/variant.hpp>
using namespace std;
typedef boost::variant<int,string> vTypeVariants;
struct vType_toN : boost::static_visitor<int>
{
int operator()(int& i) const {
return i;
}
int operator()(const string& str) const{
return str.length();
}
};
class vType{
public:
vType(const int& src) : data(src){}
vType(const std::string& src) : data(src){}
int getLength(){
return boost::apply_visitor(vType_toN(),data);
}
private:
vTypeVariants data;
};
int main(int argc, char ** argv)
{
vType x = string("2");
printf("L=%d",x.getLength());
return(0);
}
Unless you will add const to getLength():
int getLength() const{
return boost::apply_visitor(vType_toN(),data);
}
In such case an error with vast description (2 pages) appears complaining about problem with initializing first argument.
So, the question is: How to use apply_visitor inside const function?
Found out myself.
Forgot const before int in static_visitor class operator definition.
Maybe someone will find this useful as it was not easy to find this out (my original class is much bigger).
I'm hoping someone here has a workaround for this. This code compiles fine on gcc, and VS2010 SP1 (Debug x32, Release x32, Debug x64), but fails for 64 bit Release builds with a fatal error C1060: compiler is out of heap space. I'm using Boost 1.52
#include "stdafx.h"
#include <boost/variant.hpp>
#include <string>
#include <vector>
#include <map>
typedef boost::make_recursive_variant<
boost::blank,
std::string,
int,
double,
std::vector<std::vector<int> >,
std::vector<std::vector<double> >,
std::vector<std::vector<std::string> >,
std::map<std::string, std::vector<std::string> >,
std::map<std::string, std::vector<double> >,
std::map<std::string, std::vector<int> >,
std::map<std::string,boost::recursive_variant_>
>::type InfoHolder2;
class Test
{
InfoHolder2 test;
};
class Test3
{};
class Test4
{};
template <class T>
class Base
{
public:
Base(){};
virtual std::string Foo(const T& val, const std::string& header = "") const = 0;
virtual T Bar(const std::string& buffer, size_t offset = 0) const = 0;
};
template <class Payload, class iArchive, class oArchive>
class TestSer : Base<Payload>
{
public:
TestSer():Base()
{ }
virtual std::string Foo(const Payload& holder,const std::string& header = "") const
{
return "";
}
virtual Payload Bar( const std::string& buffer, size_t offset = 0) const
{
Payload retval;
return retval;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
new TestSer<Test, Test3, Test4>();
return 0;
}
This fails every time. If I reduce the number of elements the variant can handle I can get it to compile, but that isn't really preferred as this works nicely for me. If I change the class to the version below, it works. Does anyone have any ideas for workarounds?
template <class Payload, class iArchive, class oArchive>
class TestSer
{
public:
TestSer()
{ }
std::string Foo(const Payload& holder,const std::string& header = "") const
{
return "";
}
Payload Bar( const std::string& buffer, size_t offset = 0) const
{
Payload retval;
return retval;
}
};
I am getting the following compiler error:
/usr/include/boost/variant/variant.hpp:832:32: error: no match for
call to ‘(const StartsWith) (bool&)’
for the following code. Does anybody know why?
#include "boost/variant/variant.hpp"
#include "boost/variant/apply_visitor.hpp"
using namespace std;
using namespace boost;
typedef variant<bool, int, string, const char*> MyVariant;
class StartsWith
: public boost::static_visitor<bool>
{
public:
string mPrefix;
bool operator()(string &other) const
{
return other.compare(0, mPrefix.length(), mPrefix);
}
StartsWith(string const& prefix):mPrefix(prefix){}
};
int main(int argc, char **argv)
{
MyVariant s1 = "hello world!";
apply_visitor(StartsWith("hel"), s1); // << compiler error
return 0;
}
You have to provide operators for every type declared in MyVariant.