How to read an array of pairs result from libpqxx into C++ - c++

I have a Postgres function returning the type tp_m_info which contains
an array of pairs (see below). And I'm using libpqxx to connecting to
Postgres.
CREATE TYPE public.tp_m_set_id AS
(
m_id integer,
m_name text
);
CREATE TYPE public.tp_m_info AS
(
m_id integer,
m_name text,
m_value double precision,
m_is_true boolean,
original_ms tp_m_set_id[]
);
I can read int, double, str and even Boolean from the result:
iter[m_id].as<int>()
iter[m_name].c_str()
iter[m_value].as<double>()
// and bool like
std::string tmp_str = iter["m_is_true"].c_str();
if ("t" == tmp_str)
{
info.m_is_merged = true;
}
else
{
info.m_is_merged = false;
}
but I don't know how to handle "tp_m_set_id[]"
It failed with something like "std::vector<std::pair<uint32_t, std::string>>"
iter[original_ms].as<std::vector<std::pair<uint32_t, std::string>>>()
Any Idea how to get it?
In "libpq" there is binary resultFormat with:
PQexecParams() and paramFormats=1
see: https://www.postgresql.org/docs/10/libpq-exec.html
Is there a binary Format in "libpqxx" now? Has something changed in
the last 10 years?
see: http://pqxx.org/development/libpqxx/wiki/BinaryTransfers
Is there a fast way to get a block of compound data from libpqxx and
convert it to C++?

I guess the solution is
e.g. to use:
std::pair<int, int> my_pair = row["m_ints"].as<std::pair<int, int> >();
write into strconv.hxx:
template<> struct PQXX_LIBEXPORT string_traits<std::pair<int, int> >
{
static constexpr const char *name() noexcept { return "std::pair<int, int>"; }
static constexpr bool has_null() noexcept { return false; }
static bool is_null(std::pair<int, int>) { return false; }
[[noreturn]] static std::pair<int, int> null()
{ internal::throw_null_conversion(name()); }
static void from_string(const char Str[], std::pair<int, int> &Obj);
static std::string to_string(std::pair<int, int> Obj);
};
and in strconv.cxx implement:
static void from_string(const char Str[], std::pair &Obj);
static std::string to_string(std::pair Obj);
now you have to recompile libpqxx.
The problem is, the implementation of "from_string" ends up in stringparsing a string like "(9,5)". And Stringparsing is not what i want.
so i'm going to try libpq.

Related

Avoiding multiple lookups in map/unordered_map

Let's say we have an expensive function mapping string to int and want to cache results in a map.
The simplest code would be
int mapStringToIntWithCache(std::string const& s) {
static std::unordered_map<std::string, int> cache;
if (cache.count(s) > 0) return cache[s];
else return cache[s] = myExpensiveFunction(s);
}
But this has 2 lookups.
I therefore tend to write this
int mapStringToIntWithCache(std::string const& s) {
static std::unordered_map<std::string, int> cache;
size_t sizeBefore = cache.size();
int& val = cache[s];
if (cache.size() > sizeBefore) val = myExpensiveFunction(s);
return val;
}
This has only one lookup, but seems a little clumsy. Is there a better way?
Just use std::map::emplace() method:
int mapStringToIntWithCache(std::string const& s) {
static std::unordered_map<std::string, int> cache;
auto pair = cache.emplace( s, 0 );
if( pair.second )
pair.first->second = myExpensiveFunction(s);
return pair.first->second;
}
Just a note to the #Slava's answer: If you pass argument by const lvalue reference, you cannot move then from this argument if it's rvalue:
int i = mapStringToIntWithCache("rvalue argument here");
The temporary std::string argument will be copied here if insertion to cache takes place.
You can use perfect forwarding, however, if you want to maintain arguments to be of std::string type only (e.g., for implicit conversions from string literals), then you need some wrapper-helper function solution:
template <typename T>
int mapStringToIntWithCacheHelper(T&& s) {
static std::unordered_map<std::string, int> cache;
auto pair = cache.emplace( std::forward<T>(s), 0 );
if( pair.second )
pair.first->second = myExpensiveFunction(pair.first->first); // can't use s here !!!
return pair.first->second;
}
int mapStringToIntWithCache(const std::string & s) {
mapStringToIntWithCacheHelper(s);
}
int mapStringToIntWithCache(std::string && s) {
mapStringToIntWithCacheHelper(std::move(s));
}

Can a map's key be shared with part of the value?

Can a std::map's or std::unordered_map's key be shared with part of the value? Especially if the key is non-trivial, say like a std::string?
As a simple example let's take a Person object:
struct Person {
// lots of other values
std::string name;
}
std::unordered_map<std::string, std::shared_ptr<Person>> people;
void insertPerson(std::shared_ptr<Person>& p) {
people[p.name] = p;
// ^^^^^^
// copy of name string
}
std::shared_ptr<Person> lookupPerson(const std::string& name) const {
return people[name];
}
My first thought is a wrapper around the name that points to the person, but I cannot figure out how to do a lookup by name.
For your purpose, a std::map can be considered a std::set containing std::pair's which is ordered (and thus efficiently accessible) according to the first element of the pair.
This view is particularly useful if key and value elements are partly identical, because then you do not need to artificially separate value and key elements for a set (and neither you need to write wrappers around the values which select the key).
Instead, one only has to provide a custom ordering function which works on the set and extracts the relevant key part.
Following this idea, your example becomes
auto set_order = [](auto const& p, auto const& s) { return p->name < s->name; };
std::set<std::shared_ptr<Person>, decltype(set_order)> people(set_order);
void insertPerson(std::shared_ptr<Person>& p) {
people.insert(p);
}
As an alternative, here you could also drop the custom comparison and order the set by the addresses in the shared pointer (which supports < and thus can be used directly in the set):
std::set<std::shared_ptr<Person> > people;
void insertPerson(std::shared_ptr<Person>& p) {
people.insert(p);
}
Replace set by unordered_set where needed (in general you then also need to provide a suitable hash function).
EDIT: The lookup can be performed using std:lower_bound:
std::shared_ptr<Person> lookupPerson(std::string const& s)
{
auto comp = [](auto const& p, auto const& s) { return p->name < s; };
return *std::lower_bound(std::begin(people), std::end(people), s, comp);
}
DEMO.
EDIT 2: However, given this more-or-less ugly stuff, you can also follow the lines of your primary idea and use a small wrapper around the value as key, something like
struct PersonKey
{
PersonKey(std::shared_ptr<Person> const& p) : s(p->name) {}
PersonKey(std::string const& _s) : s(_s) {}
std::string s;
bool operator<(PersonKey const& rhs) const
{
return s < rhs.s;
}
};
Use it like (untested)
std::map<PersonKey, std::shared_ptr<Person> > m;
auto sptr = std::make_shared<Person>("Peter");
m[PersonKey(sptr)]=sptr;
Lookup is done through
m[PersonKey("Peter")];
Now I like this better than my first suggestion ;-)
Here's an alternative to davidhigh's answer.
struct Person {
// lots of other values
std::string name;
}
struct StrPtrCmp {
bool operator()(const std::string* a, const std::string* b) const {
return *a < *b;
}
}
std::map<const std::string*, std::shared_ptr<Person>, StrPtrCmp> people();
void insertPerson(std::shared_ptr<Person>& p) {
people[&(p.name)] = p;
}
std::shared_ptr<Person> lookupPerson(const std::string& name) const {
return people[&name];
}
And a few edits to make it work with std::unordered_map:
struct StrPtrHash {
size_t operator()(const std::string* p) const {
return std::hash<std::string>()(*p);
}
};
struct StrPtrEquality {
bool operator()(const std::string* a, const std::string* b) const {
return std::equal_to<std::string>()(*a, *b);
}
};
std::unordered_map<const std::string*, std::shared_ptr<Person>, StrPtrHash, StrPtrEquality> people();

Getting wrong result for FIND function of map

The method fails to find the char* array passed to it even though it is present in map.
When I replaced char* with std::string in map. Code works fine.
static void CreateTranslationMap();
static UString FindTranslatedString(char* propertyName);
static std::map<char*,UString> TranslationMap ;
static void CreateTranslationMap()
{
UString engString("TextAlignmentPosition");
char* transString= MSGTXT("TextAlignmentPosition");
TranslationMap.insert(std::pair<char*,UString>(transString,engString));
}
UString FindTranslatedString(char* propertyName)
{
UString NotFound("CannotFind");
std::map<char*, UString>::iterator itr;
itr = TranslationMap.begin();
itr = TranslationMap.find(propertyName);
if(itr!= TranslationMap.end())
{
return itr->second;
}
else if(itr== TranslationMap.end())
{
return NotFound;
}
}
You need to use your own custom comparator for comparing pointer to char
Use:
struct cmp_c_string
{
bool operator()(char const *lhs, char const *rhs) const
{
return std::strcmp(lhs, rhs) < 0;
}
};
std::map<char*,UString, cmp_c_string > TranslationMap ;
That's because when doing comparison for equality the map uses <.
When the Key of the map is char* you are doing comparisons of pointers (not the C-String). So you are testing to see if one pointer is less than the other pointer (ie comparing the address).
When the Key of the map is std::string you using the operator< that is defined for std::string, which actually compares the characters in the string to determine which is less than the other.
As the std::map is a template it actually takes more parameters to define how it works. The third parameters is the comparison function (which defaults to less std::less<K> which is usually operator<).
So you can use char* you just need a custom comparator operator.
bool comparitor(char const* lhs, char const* rhs)
{
return (test for less than or some other strict weak function);
}
std::map<char*, UString, comparitor> myMap;
when using char *, it just compare address.
char a[] = "hi";
char b[] = "hi";
char *c = a;
char *d = b;
c & d are different.(c != d) If you want to compare string, you should use strcmp.
But when using string, it overwrites "==" operation.
So you can just compare using "=="
string a = "hi";
string b = "hi";
a & b are same. (a == b)
You have this behavior because you use pointer to string literal which is different every time you create such a pointer. So, for example, you create 2 pointers:
char* p1 = "Hello world!";
char* p2 = "Hello world!";
While content to which p1 and p2 point is identical the pointers, themselves, are different. So p1 != p2, and you trying to store pointer in the map. You should use std::string instead or have global constants pointers which you'd use everywhere; something like:
const char* const c_transString = MSGTXT("TextAlignmentPosition");
...
TranslationMap.insert(std::pair<char*,UString>(c_transString, engString));
...
FindTranslatedString(c_transString)
Just replace char* to const char* because the map data type always take the string in const form . I took your example and it is running in my terminal. So the new code is :
#include<iostream>
using namespace std;
static void CreateTranslationMap();
static string FindTranslatedString(const char* propertyName);
static std::map<const char*,string> TranslationMap ;
static void CreateTranslationMap()
{
string engString("TextAlignmentPosition");
const char* transString= ("1");
TranslationMap.insert(std::pair<const char*,string>(transString,engString));
}
string FindTranslatedString(const char* propertyName)
{
string NotFound("CannotFind");
std::map<const char*, string>::iterator itr;
itr = TranslationMap.begin();
itr = TranslationMap.find(propertyName);
if(itr!= TranslationMap.end())
{
return itr->second;
}
else if(itr== TranslationMap.end())
{
return NotFound;
}
}
int main()
{
CreateTranslationMap();
string s =FindTranslatedString("1");
cout<<s<<endl;
return 0;
}

c++ STL map.find() or map.operator[] cannot be used in class member function with const qualifier

I'm confused by the following code, why it cannot be successfully compiled?
class Test {
public:
int GetValue( int key ) const
{
return testMap[key];
}
map<const int, const int> testMap;
};
There is always a compling error:
error C2678: binary '[': no ​​operator found which takes "const std :: map <_Kty,_Ty>" type of the left operand operator (or there is no acceptable conversion).
I've tried to put const qualifier everywhere but it still couldn't pass. Could you tell me why?
operator[] is not const, because it inserts an element if one doesn't already exist with the given key. find() does have a const overload, so you can call it with a const instance or via a const reference or pointer.
In C++11, there is std::map::at(), which adds bounds checking and raises an exception if an element with the given key is not present. So you can say
class Test {
public:
int GetValue( int key ) const
{
return testMap.at(key);
}
std::map<const int, const int> testMap;
};
Otherwise, use find():
int GetValue( int key ) const
{
auto it = testMap.find(key);
if (it != testMap.end()) {
return it->second;
} else {
// key not found, do something about it
}
}
You got an excellent answer by juanchopanza
Just wanted to show a boost way to return something that's not valid
with boost::optional you can return empty type
#include<boost\optional.hpp>
...
boost::optional<int> GetValue(int key){
auto it = testMap.find(key);
if (it != testMap.end()) {
return it->second;
} else {
return boost::optional<int>();
}
}
boost::optional<int> val = GetValue(your_key);
if(!val) //Not empty
{
}

String to enum in C++

Is there a way to associate a string from a text file with an enum value?
The problem is: I have a few enum values stored as string in a text file which I read on the fly on meeting some condition... Now I want to assign the read value to an enum.
What is the most effective way to do so? It doesn't need to be the simplest approach.
You can set up a map that you can use over and over:
template <typename T>
class EnumParser
{
map <string, T> enumMap;
public:
EnumParser(){};
T ParseSomeEnum(const string &value)
{
map <string, T>::const_iterator iValue = enumMap.find(value);
if (iValue == enumMap.end())
throw runtime_error("");
return iValue->second;
}
};
enum SomeEnum
{
Value1,
Value2
};
EnumParser<SomeEnum>::EnumParser()
{
enumMap["Value1"] = Value1;
enumMap["Value2"] = Value2;
}
enum OtherEnum
{
Value3,
Value4
};
EnumParser<OtherEnum>::EnumParser()
{
enumMap["Value3"] = Value3;
enumMap["Value4"] = Value4;
}
int main()
{
EnumParser<SomeEnum> parser;
cout << parser.ParseSomeEnum("Value2");
}
std::map< string, enumType> enumResolver;
I agree with many of the answers that std::map is the easiest solution.
If you need something faster, you can use a hash map. Perhaps your compiler already offers one, such as hash_map or the upcoming standard unordered_map, or you can get one from boost. When all the strings are known ahead of time, perfect hashing can be used as well.
Accepted answer doesn't contain full listing. I'm adding EnumParser.h which I created from the accepted answer, hope it can help
#include <string>
#include <map>
using namespace std;
template <typename T> class EnumParser
{
map<string, T> enumMap;
public:
EnumParser(){};
T ParseSomeEnum(const string &value)
{
typename map <string, T>::const_iterator iValue = enumMap.find(value);
if (iValue == enumMap.end())
throw runtime_error("");
return iValue->second;
}
};
Usage is simple:
enum FieldType
{
Char,
Integer,
Long,
Fixed,
Price,
Date,
Time
};
EnumParser<FieldType>::EnumParser()
{
enumMap["Char"] = Char;
enumMap["Integer"] = Integer;
enumMap["Long"] = Long;
enumMap["Fixed"] = Fixed;
enumMap["Price"] = Price;
enumMap["Date"] = Date;
enumMap["Time"] = Time;
}
use:
EnumParser<FieldType> fieldTypeParser;
FieldType val = fieldTypeParser.ParseSomeEnum(stringValue)
Look at Boost.Bimap, it provides bidirectional associations between two sets of values.
You can also choose the underlying container.
Using a std::map raises the question: how does the map get initialised? I would rather use a function:
enum E { A, B };
E f( const std::string & s ) {
if ( s == "A" ) {
return A;
}
else if ( s == "B" ) {
return B;
}
else {
throw "Your exception here";
}
}
Is this what you want? The initialization is straight, and no instantiation is needed.
usage:
enum SomeEnum
{
ENUM_ONE,
ENUM_TWO,
ENUM_THREE,
ENUM_NULL
};
DEFINE_PAIRLIST(CEnumMap, SomeEnum)
INIT_PAIRLIST(CEnumMap)=
{
{"One", ENUM_ONE},
{"Two", ENUM_TWO},
{"Three", ENUM_THREE},
{"", ENUM_NULL}
};
main{
// Get enum from string
SomeEnum i = CEnumMap::findValue("One");
// Get string from enum
SomeEnum eee = ENUM_ONE;
const char* pstr = CEnumMap::findKey(eee);
...
}
library:
template <class T>
struct CStringPair
{
const char* _name;
T _value;
};
template <class T, class Derived>
struct CStringPairHandle
{
typedef CStringPair<T> CPair;
static const CStringPair<T> * getPairList(){
return Derived::implementation();
}
static T findValue(const char* name){
const CStringPair<T> * p = getPairList();
for (; p->_name[0]!=0; p++)
if (strcmp(name,p->_name)==0)
break;
return p->_value;
}
static const char* findKey(T value){
const CStringPair<T> * p = getPairList();
for (; p->_name[0]!=0; p++)
if (strcmp(value,p->_value)==0)
break;
return p->_name;
};
};
#define DEFINE_PAIRLIST(name, type) struct name:public CStringPairHandle<type, name>{ \
static CPair _pairList[]; \
static CPair* implementation(){ \
return _pairList; \
}};
#define INIT_PAIRLIST(name) name::CPair name::_pairList[]
Parse the string yourself, match the string with a value (which is also an index to a map<string, enum>.
You can calculate the hash of the string and then use this:
template <typename H, typename E>
E map_hash(H const key, std::initializer_list<std::pair<H, E>> const il)
{
auto const i(
std::find_if(il.begin(),
il.end(),
[key](auto& p)
{
return p.first == key;
}
)
);
assert(i != il.end());
return i->second;
}
Using C++ reflection library from here: https://github.com/tapika/cppreflect
You can - include library like this:
#include "cppreflect/cppreflect.h"
Basic usage:
Declare enumeration:
DECLARE_ENUM( enumName,
// Prefix for all enums, "" if no prefix used.
"myenum_",
myenum_enumValue1,
myenum_enumValue2,
myenum_enumValue3 = 5,
// comment
myenum_enumValue4
);
Conversion logic:
From enumeration to string:
printf( EnumToString(myenum_enumValue3).c_str() );
=> "enumValue3"
From string to enumeration:
enumName value;
if( !StringToEnum("enumValue4", value) )
printf("Conversion failed...");
=>
value == myenum_enumValue4
Main / core functionality resides in here:
https://github.com/tapika/cppreflect/blob/master/cppreflect/enumreflect.h