How to attach to existing shared memory from Qt? - qt

I have created a shared memory segment with the help of a binary in C and written some data into it. Now I want read that data from Qt. How to attach to existing shared memory from Qt?

QSharedMemory isn't really meant to interoperate with anything else. On Unix, it is implemented via SYSV shared memory, but it passes Qt-specific arguments to ftok:
::ftok(filename.constData(), qHash(filename, proj_id));
You could emulate this behavior in your C code, but I don't think it's necessary.
Instead of opening a shared memory segment, simply map a file to memory, and access it from multiple processes. On Qt, QFile::map does what you need.
The example below shows both techniques: using SYSV shared memory and using memory-mapped files:
// https://github.com/KubaO/stackoverflown/tree/master/questions/sharedmem-interop-39573295
#include <QtCore>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <stdexcept>
#include <string>
First, let's have a shared data structure.
struct Data {
int a = 1;
bool b = true;
char c = 'S';
bool operator==(const Data & o) const { return o.a == a && o.b == b && o.c == c; }
static void compare(const void * a, const void * b) {
auto data1 = reinterpret_cast<const Data*>(a);
auto data2 = reinterpret_cast<const Data*>(b);
Q_ASSERT(*data1 == *data2);
}
};
We definitely want error checking, so let's add some helpers that make that easier:
void check(bool ok, const char * msg, const char * detail) {
if (ok) return;
std::string str{msg};
str.append(": ");
str.append(detail);
throw std::runtime_error{str};
}
void check(int f, const char * msg) { check(f != -1, msg, strerror(errno)); }
void check(void * f, const char * msg) { check(f != MAP_FAILED, msg, strerror(errno)); }
void check(bool rc, const QSharedMemory & shm, const char * msg) { check(rc, msg, shm.errorString().toLocal8Bit()); }
void check(bool rc, const QFile & file, const char * msg) { check(rc, msg, file.errorString().toLocal8Bit()); }
And we need RAII wrappers for C APIs:
struct noncopyable { Q_DISABLE_COPY(noncopyable) noncopyable() {} };
struct ShmId : noncopyable {
int id;
ShmId(int id) : id{id} {}
~ShmId() { if (id != -1) shmctl(id, IPC_RMID, NULL); }
};
struct ShmPtr : noncopyable {
void * ptr;
ShmPtr(void * ptr) : ptr{ptr} {}
~ShmPtr() { if (ptr != (void*)-1) shmdt(ptr); }
};
struct Handle : noncopyable {
int fd;
Handle(int fd) : fd{fd} {}
~Handle() { if (fd != -1) close(fd); }
};
Here's how to interoperates SYSV shared memory sections between C and Qt. Unfortunately, unless you reimplement qHash in C, it's not possible:
void ipc_shm_test() {
QTemporaryFile shmFile;
check(shmFile.open(), shmFile, "shmFile.open");
// SYSV SHM
auto nativeKey = QFile::encodeName(shmFile.fileName());
auto key = ftok(nativeKey.constData(), qHash(nativeKey, 'Q'));
check(key, "ftok");
ShmId id{shmget(key, sizeof(Data), IPC_CREAT | 0600)};
check(id.id, "shmget");
ShmPtr ptr1{shmat(id.id, NULL, 0)};
check(ptr1.ptr, "shmat");
new (ptr1.ptr) Data;
// Qt
QSharedMemory shm;
shm.setNativeKey(shmFile.fileName());
check(shm.attach(QSharedMemory::ReadOnly), shm, "shm.attach");
auto ptr2 = shm.constData();
Data::compare(ptr1.ptr, ptr2);
}
Here's how to interoperate memory-mapped files:
void mmap_test() {
QTemporaryFile shmFile;
check(shmFile.open(), "shmFile.open");
shmFile.write({sizeof(Data), 0});
check(true, shmFile, "shmFile.write");
check(shmFile.flush(), shmFile, "shmFile.flush");
// SYSV MMAP
Handle fd{open(QFile::encodeName(shmFile.fileName()), O_RDWR)};
check(fd.fd, "open");
auto ptr1 = mmap(NULL, sizeof(Data), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd.fd, 0);
check(ptr1, "mmap");
new (ptr1) Data;
// Qt
auto ptr2 = shmFile.map(0, sizeof(Data));
Data::compare(ptr1, ptr2);
}
And finally, the test harness:
int main() {
try {
ipc_shm_test();
mmap_test();
}
catch (const std::runtime_error & e) {
qWarning() << e.what();
return 1;
}
return 0;
}

Related

confusing pointer error while implementing linked list

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define MALLOC(p,s) {\
if (!((p) = malloc(s))) { \
fprintf(stderr, "insufficient memory");\
exit(EXIT_FAILURE);\
}\
}
#define IS_EMPTY(first) (!first)
typedef struct listNode* listPointer;
typedef struct listNode {
int data;
listPointer link;
}listNode;
void printList(listPointer first);
int main(void)
{
int x;
int tmpData;
listPointer first = NULL;
listPointer tmpLink = NULL;
FILE* fp = NULL;
if (!(fp = fopen("in.txt", "r"))) {
fprintf(stderr, "cannot open the file");
exit(EXIT_FAILURE);
}
while (!feof(fp)) {
fscanf(fp, "%d", &tmpData);
MALLOC(tmpLink, sizeof(listNode));
if (IS_EMPTY(first)) {
MALLOC(first, sizeof(listNode));
*tmpLink = *first;
}
tmpLink->data = tmpData;
tmpLink = tmpLink->link;
}
printList(first);
}
void printList(listPointer first)
{
for (; first; first = first->link) {
printf("%d ", first->data);
}
printf("\n");
}
We know that we can implement the insert function.
But I'm really curious about why this doesn't work.
What "first" refers to and what "tmpLink" refers to is the same
After implementing the link list while updating tmpLink,
I'm going to use "first" to print later.
I've spent almost a day just thinking about this, and I've tried debugging it, but I don't know why.

Updating a File (KeyValueEEPROM) to ArduinoJson 6

I am an engineer not a programmer, but due to circumstance I am fix the programming for a project I am working on.
This is where I am currently stuck. The KeyValueEEPROM.h is used to store the collected data by the main program, and then read from to upload to a SQL database.
Somewhere in this process I am losing the data collected by the sensor. I believe I isolated the problem to the referenced file.
the first version was made using ArduinoJson 5 (I am not the original creator). This is incompatible with the current program, as ArduinoJson 5 has been upgraded to the 6th version.
The other one is edited by me to use ArduinoJson 6.
I had to change the file to get the codes to compile (which it now does), but I am not sure if I was able to preserve the functions.
I would like someone to point out any mistakes I made during the 'update'.
ArduinoJson5 Version:
#ifndef KeyValueEEPROM_h
#define KeyValueEEPROM_h
#include <Arduino.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#ifndef KeyValueEEPROM_SIZE
#if defined(ESP8266) || defined(ESP32) || defined(__AVR_ATmega1280__) ||
defined(__AVR_ATmega2560__)
#define KeyValueEEPROM_SIZE 4096
#endif
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32U4__)
#define KeyValueEEPROM_SIZE 1024
#endif
#if defined(__AVR_ATmega168__)
#define KeyValueEEPROM_SIZE 512
#endif
#endif
class KeyValueEEPROMClass {
public:
// Public methods
void begin() {
// If this method was already called, return.
if (LibraryStartet) return;
// Read JSON data from the EEPROM.
String json = readEEPROM();
// Parse JSON data.
root = &jsonBuffer.parseObject(json);
// If JSON couldn't be parsed,
if (!root->success()) {
// then create an new key-value store.
clear();
}
};
void clear() {
// Clear buffer.
jsonBuffer.clear();
// Create JSON root object.
root = &jsonBuffer.createObject();
};
void remove(String key) {
// Remove key.
root->remove(key);
};
void apply() {
// Clear the EEPROM.
clearEEPROM();
// Extract JSON data from the root object.
String json;
root->printTo(json);
// Write JSON data to the EEPROM.
writeEEPROM(json);
};
bool exists(String key) {
return root->containsKey(key);
};
template <typename T>
T get(String key) {
T value = (*root)[key].as<T>();
return value;
};
template <typename T>
void set(String key, T value) {
(*root)[key] = value;
};
private:
// Private methods
void clearEEPROM() {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Write all bytes to zero.
for (int i = 0; i < KeyValueEEPROM_SIZE; i++) EEPROM.write(i, '\0');
// End EEPROM library.
EEPROM.end();
};
String readEEPROM() {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Read JSON data until null-termination.
String string = ""; unsigned int i; unsigned char byte;
for (i = 0; i < KeyValueEEPROM_SIZE; i++) {
byte = EEPROM.read(i);
if (byte == '\0') {
break;
} else {
string += char(byte);
}
}
// End EEPROM library.
EEPROM.end();
// Return JSON data.
return string;
};
void writeEEPROM(String json) {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Write JSON data.
unsigned int i;
for (i = 0; i < json.length(); i++) {
EEPROM.write(i, json.charAt(i));
}
// If the length of the JSON data is smaller than the EEPROM size,
if (json.length() < KeyValueEEPROM_SIZE)
// than write at the last position a null-termination.
EEPROM.write(json.length(), '\0');
// End EEPROM library.
EEPROM.end();
}
// Properties
StaticJsonBuffer<KeyValueEEPROM_SIZE> jsonBuffer;
JsonObject *root;
bool LibraryStartet = false;
};
static KeyValueEEPROMClass KeyValueEEPROM;
extern KeyValueEEPROMClass KeyValueEEPROM;
#endif
ArduinoJson6 Version
#ifndef KeyValueEEPROM_h
#define KeyValueEEPROM_h
#include <Arduino.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#ifndef KeyValueEEPROM_SIZE
#if defined(ESP8266) || defined(ESP32) || defined(__AVR_ATmega1280__) ||
defined(__AVR_ATmega2560__)
#define KeyValueEEPROM_SIZE 4096
#endif
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32U4__)
#define KeyValueEEPROM_SIZE 1024
#endif
#if defined(__AVR_ATmega168__)
#define KeyValueEEPROM_SIZE 512
#endif
#endif
class KeyValueEEPROMClass {
public:
// Public methods
void begin() {
// If this method was already called, return.
if (LibraryStartet) return;
// Read JSON data from the EEPROM.
String json = readEEPROM();
// Parse JSON data.
DeserializationError error = deserializeJson(doc,json);
// If JSON couldn't be parsed,
if (error) {
// then create an new key-value store.
clear();
}
};
void clear() {
// Clear buffer.
doc.clear();
// Create JSON root object.
// doc = &doc.createObject();
};
void remove(String key) {
// Remove key.
doc.remove(key);
};
void apply() {
// Clear the EEPROM.
clearEEPROM();
// Extract JSON data from the root object.
String json;
//doc.
serializeJson(doc, json);
// Write JSON data to the EEPROM.
writeEEPROM(json);
};
bool exists(String key) {
return doc.containsKey(key);
};
template <typename T>
T get(String key) {
T value = doc[key].as<T>();
return value;
};
template <typename T>
void set(String key, T value) {
doc[key] = value;
};
private:
// Private methods
void clearEEPROM() {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Write all bytes to zero.
for (int i = 0; i < KeyValueEEPROM_SIZE; i++) EEPROM.write(i, '\0');
// End EEPROM library.
EEPROM.end();
};
String readEEPROM() {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Read JSON data until null-termination.
String string = ""; unsigned int i; unsigned char byte;
for (i = 0; i < KeyValueEEPROM_SIZE; i++) {
byte = EEPROM.read(i);
if (byte == '\0') {
break;
} else {
string += char(byte);
}
}
// End EEPROM library.
EEPROM.end();
// Return JSON data.
return string;
};
void writeEEPROM(String json) {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Write JSON data.
unsigned int i;
for (i = 0; i < json.length(); i++) {
EEPROM.write(i, json.charAt(i));
}
// If the length of the JSON data is smaller than the EEPROM size,
if (json.length() < KeyValueEEPROM_SIZE)
// than write at the last position a null-termination.
EEPROM.write(json.length(), '\0');
// End EEPROM library.
EEPROM.end();
}
// Properties
StaticJsonDocument<KeyValueEEPROM_SIZE> doc;
//JsonObject *doc;
JsonObject object = doc.to<JsonObject>();
bool LibraryStartet = false;
};
static KeyValueEEPROMClass KeyValueEEPROM;
extern KeyValueEEPROMClass KeyValueEEPROM;
#endif
This is also my first post here, so if I have formatting issues or lack clarity I apologize.
Thanks,

crash on ubuntu but normally on Mac when get instance out from sqlie blob

I had saved a class instance in blob field using sqlite-db.
The table policyRule have only one field which is "blob" type.
Now I get it out from the table, which is run normally in macOS but ubuntu.
Would you give me some advice?
#include <string.h>
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <string>
using namespace std;
class SENDFILERULE
{
public:
string a;
int b;
string c;
};
void doTest()
{
sqlite3* conn = NULL;
int result = sqlite3_open("mytest.db",&conn);
if (result != SQLITE_OK)
{
sqlite3_close(conn);
return;
}
char createTableSQL[1024];
sprintf(createTableSQL,"%s","CREATE TABLE IF NOT EXISTS policyRule (rule blob)");
sqlite3_stmt* stmt = NULL;
//8.read from database
const char* selectSQL = "SELECT * FROM policyRule";
stmt = NULL;
if(sqlite3_prepare_v2(conn,selectSQL,strlen(selectSQL),&stmt,NULL) !=SQLITE_OK)
{
if (stmt)
sqlite3_finalize(stmt);
sqlite3_close(conn);
return;
}
do
{
int ret = sqlite3_step(stmt);
if (ret == SQLITE_ROW)
{
char* buf =(char*)malloc(1024);
memset(buf,0,1024);
buf = (char*)sqlite3_column_blob(stmt,0);
SENDFILERULE* pRule =reinterpret_cast<SENDFILERULE*>(buf);
printf("%s %d %s \n",pRule->a.c_str(),pRule->b,pRule->c.c_str());
}
else if(ret == SQLITE_DONE)
{
printf("select end \n");
break;
}
}while(1);
sqlite3_finalize(stmt);
}
int main()
{
doTest();
return 0;
}
when I print the pRule->b, it is ok, but the string type.

What is this error about?

ALL,
I'm using Anjuta to do my development.
I created a project for my main application, and then made 2 more: 1 for the static library (libdbinterface.a) and 1 for the dynamic library (libsqlite_lib.so).
Both those libraries contains one exported class each: libdbinterface.a - class Database, libsqlite_lib.so - public SQLiteDatabase : public Database.
Now I'm trying to link libdbinterface.a to libsqlite_lib.so.
So in Anjuta I added following to the "Linker Option" for the target libsqlite_lib.so:
-L/home/igor/dbhandler/Debug/dbinterface -ldbinterface
However, trying to compile I received following error from linker:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: /home/igor/dbhandler/Debug/dbinterface/libdbinterface.a(database.o): relocation R_X86_64_32S against `_ZTV8Database' can not be used when making a shared object; recompile with -fPIC
I tried to recompile libsqlite_lib.so with -fPIC explicitely added to "C++ Options" of that project but that didn't solve it - I still receive the same error.
Unfortunately trying to Google on how to link .a and .so is not helpful.
Can someone sched some light on how to fix this error?
TIA.
[EDIT]
libsqlite_lib.so Makefile - https://bpaste.net/show/1495231e58cc
libdbinterface.a Makefile - https://bpaste.net/show/3a71c119d0fc
libdbinterface.a contains 2 files databse.h:
#ifndef DBMANAGER_DATABASE
#define DBMANAGER_DATABASE
class Field
{
public:
Field(const std::string &columnName, const std::string &columnType, const std::string &columnDefaultValue = "", const bool columnIsNull = false, const bool columnPK = false)
{
column_name = columnName;
column_type = columnType;
column_defaultValue = columnDefaultValue;
column_isNull = columnIsNull;
column_pk = columnPK;
}
private:
std::string column_name, column_type, column_defaultValue;
bool column_isNull, column_pk;
};
struct FKField
{
FKField(const std::string &table_name, const std::string &original_field, const std::string &referenced_field)
{
tableName = table_name;
originalField = original_field;
referencedField = referenced_field;
}
std::string tableName, originalField, referencedField;
};
class Table
{
public:
Table(const std::string &tableName, const std::vector<Field> &tableFields, const std::map<int,std::vector<FKField> > &foreignKeys)
{
table_name = tableName;
table_fields = tableFields;
foreign_keys = foreignKeys;
}
const std::string &GetTableName() { return table_name; }
std::map<int,std::vector<FKField> > &GetForeignKeyVector() { return foreign_keys; }
private:
std::string table_name;
std::vector<Field> table_fields;
std::map<int,std::vector<FKField> > foreign_keys;
};
#ifdef WIN32
class __declspec(dllexport) Database
#else
class Database
#endif
{
private:
struct Impl;
Impl *pimpl;
public:
Database();
virtual ~Database();
Impl &GetTableVector();
static void *operator new(std::size_t size);
static void operator delete(void *ptr, std::size_t size);
virtual int Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg);
virtual int GetTableListFromDb(std::string &) { return 0; }
};
#endif
and database.cpp:
#ifdef WIN32
#include <windows.h>
#endif
#include <map>
#include <vector>
#include <string>
#include <sqlext.h>
#include "database.h"
struct Database::Impl
{
std::vector<Table> m_tables;
};
Database::Database() : pimpl( new Impl )
{
}
Database::~Database()
{
delete pimpl;
}
void *Database::operator new(std::size_t size)
{
return ::operator new( size );
}
void Database::operator delete(void *ptr, std::size_t size)
{
return ::operator delete( ptr );
}
Database::Impl &Database::GetTableVector()
{
return *pimpl;
}
int Database::Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg)
{
selectedDSN = selectedDSN;
errorMsg = errorMsg;
return 0;
}
libsqlite_lib.so has also 2 files: database_sqlite.h
#ifndef DBMANAGER_SQLITE
#define DBMANAGER_SQLITE
#ifdef WIN32
class __declspec(dllexport) SQLiteDatabase : public Database
#else
class SQLiteDatabase : public Database
#endif
{
public:
SQLiteDatabase();
~SQLiteDatabase();
virtual int Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg);
virtual int GetTableListFromDb(std::vector<std::wstring> &errorMsg);
protected:
void GetErrorMessage(int code, std::wstring &errorMsg);
private:
sqlite3 *m_db;
};
#endif
and database_sqlite.cpp with the actual implementation.
[/EDIT]
Well, apparently the solution is to rebuild static library with "-fPIC", not the dynamic one.
Thank you for reading.

Constant container (map) - eliminate heap allocation

If I create a static const std::map, it will allocate memory on heap. Following code throws bad_alloc:
#include <iostream>
#include <map>
class A {
public:
static const std::map<int, int> a;
};
const std::map<int, int> A::a = { { 1, 3} , { 2, 5} };
void* operator new ( std::size_t count )
{
throw std::bad_alloc();
}
int
main (void)
{
for(auto &ai: A::a) {
std::cout << ai.first << " " << ai.second << "\n";
}
return 0;
}
Is it possible to create this constant map somehow without having memory allocation?
As Igor Tandetnik suggested, a custom allocator would do the trick. The following is a quick'n'dirty example of a simple linear allocator, which returns memory slots from a static buffer:
#include <iostream>
#include <map>
#include <cassert>
template <typename T>
class LinearAllocator {
static constexpr size_t _maxAlloc = 1<<20;
using Buffer = std::array<T, _maxAlloc>;
using FreeList = std::array<bool, _maxAlloc>;
static Buffer _buffer;
static FreeList _allocated;
public:
typedef T* pointer;
typedef T value_type;
template<typename U>
struct rebind { typedef LinearAllocator<U> other; };
pointer allocate(size_t /*n*/, const void *hint=0) {
for(size_t i = 0; i < _maxAlloc; ++i) {
if(!_allocated[i]) {
_allocated[i] = true;
return &_buffer[i];
}
}
throw std::bad_alloc();
}
void deallocate(pointer p, size_t /*n*/) {
assert(p >= &_buffer[0] && p < &_buffer[_maxAlloc]);
_allocated[p-&_buffer[0]] = false;
}
LinearAllocator() throw() { }
LinearAllocator(const LinearAllocator &a) throw() { }
template <class U>
LinearAllocator(const LinearAllocator<U> &a) throw() { }
~LinearAllocator() throw() { }
};
template <typename T>
typename LinearAllocator<T>::Buffer LinearAllocator<T>::_buffer;
template <typename T>
typename LinearAllocator<T>::FreeList LinearAllocator<T>::_allocated;
using MyMap = std::map<int, int, std::less<int>,
LinearAllocator<std::pair<int,int> > >;
// make sure we notice if new gets called
void* operator new(size_t size) {
std::cout << "new called" << std::endl;
}
int main() {
MyMap m;
m[0] = 1; m[1] = 3; m[2] = 8;
for(auto & p : m)
std::cout << p.first << ": " << p.second << std::endl;
return 0;
}
Output:
0: 1
1: 3
2: 8
Note that this allocator will only handle requests for single slots at a time. I'm sure you will figure out how to extend it according to your requirements.

Resources