Reputation: 3004
I have the following problem:
static std::map
Something along the lines:
#include <cstddef>
#include <string>
#include <map>
template <class T> class block {
private:
T *ptr;
std::string name;
std::size_t size;
static std::map<std::string, block<T>&> blocks;
public:
block(std::string _name, T *_ptr, std::size_t _size)
: name(_name)
, ptr(_ptr)
, size(_size)
{
blocks[_name] = this;
}
virtual ~block() {}
static block<T> &find(std::string key) {
return blocks[key];
}
};
Now I have the following problem:
write a function that, given the block name, an index and a unsigned char []
, should fill a single element element in the right block taking as many bytes "as needed".
Signature of such a function (or member) should be something like:
void set_element(std::string block_name, int index, void *source);
Is there a clean way to do this?
Ideally the only prerequisite should be there actually are enough bytes in the pointed array (I can add a std::size_t available_bytes
to function signature to actually check and throw
an error if there aren't enough bytes available).
If useful: reason why I need this is the I receive "unstructured" data from network and that's a byte stream I need to parse piece by piece.
To be more precise: the input stream i get is composed by a sequence of:
There may be multiple "blocks" stick back-to-back in the input stream so it would be useful to have a return value stating either the number of bytes used or a pointer to first unused.
It's assumed binary data it comes from a compatible architecture and thus it is a correct representation.
The block name implicitly gives the size and characteristics of the element.
Incoming data is not supposed to be aligned; OTOH array pointed in block::ptr is supposed to be correctly aligned so I can access it with normal pointer arithmetic.
Note: the above example code is not good because it will produce separate blocks
for different template arguments while I need all names to be thrown in the same bag; I assume I will need to implement a parent class, but it's unclear exactly how to do it: I will update if I come to it.
Upvotes: 2
Views: 84
Reputation: 3004
I went through the exercise of deriving from a base class.
Here is a piece of code actually compiling:
#ifndef AWBLOCK_H_
#define AWBLOCK_H_
#include <stdexcept>
#include <cstddef>
#include <string>
#include <map>
namespace AW {
class generic_block {
private:
static std::map<std::string, generic_block*> blocks;
protected:
std::string name;
std::size_t size;
virtual int _set(int, void *) = 0;
public:
generic_block(std::string _name, std::size_t _size)
: name(_name)
, size(_size)
{
blocks[_name] = this;
}
virtual ~generic_block() {}
static generic_block *find(std::string key) {
return blocks[key];
}
void *set(std::string _name, int _index, void *data) {
if (blocks.find(_name) == blocks.end()) {
throw std::invalid_argument("Unknown block '"+_name+"'");
}
if (_index >= (int)size) {
throw std::out_of_range("Index "+std::to_string(_index)+" exceeds size of block '"+_name+"' ("+std::to_string(size)+")");
}
int n = blocks[_name]->_set(_index, data);
return ((char *)data)+n;
}
virtual int count(void) {
return size;
}
};
template <class T> class AWblock: public generic_block {
private:
T *ptr;
public:
AWblock(std::string _name, T *_ptr, std::size_t _size)
: generic_block(_name, _size)
, ptr(_ptr)
{
}
virtual ~AWblock() {}
int _set(int _index, void *_data) {
int n = sizeof(*ptr);
memcpy(ptr+_index, _data, n);
return n;
}
};
} /* namespace AW */
#endif /* AWBLOCK_H_ */
Minimal test program:
#include "gtest/gtest.h"
#include "AWblock.h"
#define countof(__) (sizeof(__)/sizeof(__[0]))
namespace AW {
std::map<std::string, generic_block*> generic_block::blocks;
int int_block[22];
AWblock<int32_t> _int_block("int-block", int_block, countof(int_block));
TEST(AWblock, HandleCount) {
EXPECT_EQ(_int_block.count(), 22);
}
} /* namespace AW */
apparently works fine:
Running main() from /usr/src/googletest/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from AWblock
[ RUN ] AWblock.HandleCount
[ OK ] AWblock.HandleCount (0 ms)
[----------] 1 test from AWblock (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.
If there's a better approach I will gladly accept it, otherwise I will accept my own answer in a few days.
Upvotes: 1