c++ pointers & Arrays - c++

I am really at my wits end with trying to get pointers to work on c++. I have searched so many questions, but I just cant understand these things.
I am trying to do the equivalent of this python code in c++ :
class Node:
def __init__(self,isBase,channelList):
self.isBase = isBase
self.channelList = channelList
def getChannelList(self):
return self.channelList
def isBase(self):
return self.isBase
Channel list is the difficult bit. It is an array of integers.
I know that this will be passed as a pointer to my class declaration. I want to be able to store this in a class variable, and be able to get and set it on command.
The C++ Code is as follows:
#include "Arduino.h"
#include "Node.h"
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
int *_channelList;
Node::Node(boolean isBase, int *channelList)
{
_isBase = isBase;
int i=0;
while(channelList[i]!='\0'){
_channelList[i] = channelList[i];
i++;
}
_channelList[i+1] = '\0';
}
boolean Node::isBase(){
return _isBase;
}
int* Node::getChannelList(){
return _channelList;
}

Justin Time's answer is the correct way to implement this in C++ (using arrays and vectors natively handled by C++)
I just have to add this solution, which is the correct way to implement what you tried to do in C (i.e. using char arrays).
There are two problems in your code
_channelList is NOT tied to the Node object, but is a somewhat static member.
_channelList is never allocated, so it points to nothing.
not a real problem, but usually '\0' is the string terminator. Ok, it maps to 0, but you should just use a 0 here
There are two solutions here. The first one is to give _channelList a fixed MAXIMUM size (maximum means that if the passed channelList is shorter you will get a shorter list, ok, but the allocated memory will still be the maximum one).
// File Node.h
#define MAXIMUM_CHANNELS 10
class Node {
public:
Node(boolean isBase, int *channelList);
boolean isBase();
int* getChannelList();
private:
int _channelList[MAXIMUM_CHANNELS + 1]; // Last one is the terminator
};
// File Node.cpp
include "Arduino.h"
#include "Node.h"
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
Node::Node(boolean isBase, int *channelList)
{
_isBase = isBase;
int channelListLength;
// Get channel list lenght
for (channelListLength = 0; channelList[channelListLength] != 0; channelListLength++);
if (channelListLength > MAXIMUM_CHANNELS)
channelListLength = MAXIMUM_CHANNELS;
int i;
for (i = 0; i < channelListLength; i++)
_channelList[i] = channelList[i];
_channelList[channelListLength] = 0; // Enforce the last one to be a 0
}
boolean Node::isBase(){
return _isBase;
}
int* Node::getChannelList(){
return _channelList;
}
The second method dynamically allocates the memory for the array. You should, however, dispose it when you are done with the object (in the destructor). This means that if you create the Node variable you are ok (for instance, Node mynode;). If, however, you dynamically allocate it (with Node *p_mynode = new Node(); you will need to call a delete on it when you are done.
// File Node.h
class Node {
public:
Node(boolean isBase, int *channelList);
~Node(); // destructor (called at object destruction)
boolean isBase();
int* getChannelList();
private:
int *_channelList;
};
// File Node.cpp
include "Arduino.h"
#include "Node.h"
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
Node::Node(boolean isBase, int *channelList)
{
_isBase = isBase;
int channelListLength;
// Get channel list lenght
for (channelListLength = 0; channelList[channelListLength] != 0; channelListLength++);
_channelList = (int*)malloc((channelListLength+1)*sizeof(int));
if (_channelList != NULL)
{
int i;
for (i = 0; i <= channelListLength; i++)
_channelList[i] = channelList[i];
// No need to enforce the last one to be a 0
}
}
~Node()
{
free(_channelList);
}
boolean Node::isBase(){
return _isBase;
}
int* Node::getChannelList(){
return _channelList;
}
Note, however, that if the malloc fails you will have a NULL pointer. In theory it should not fail, unless you run out of ram...
Just one more thing. Using a 0-terminated int array is not a good idea, because if you have something like this { 15, 3, 0, 5, 10 } and then the terminator you will just get the first two numbers. It would be MUCH better to explicitely tell the array size (and save it in the object)
// File Node.h
class Node {
[...]
private:
int *_channelList;
int _channelListLength;
};
// File Node.cpp
Node::Node(boolean isBase, int *channelList, int channelListLength)
{
_isBase = isBase;
_channelListLength = channelListLength;
_channelList = (int*)malloc((_channelListLength)*sizeof(int));
if (_channelList != NULL)
{
int i;
for (i = 0; i <= _channelListLength; i++)
_channelList[i] = channelList[i];
}
}
...
int Node::getChannelListLength(){
return _channelListLength;
}

Assuming that channelList is null-terminated, and that _channelList is supposed to be a class member, you could try this:
#include <algorithm> // For std::copy().
// ...
template<size_t N> Node::Node(bool isBase, const int (&channelList)[N]) : _isBase(isBase)
{
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
}
Unless I'm missing something, that should take a C-style int array of any size, and copy it into _channelList. If the passed array is larger than _channelList, it may cause issues. If possible, you would be better off using a std::array if the size is predetermined, or a std::vector if it isn't.
If the size is fixed:
#include <array>
#include <algorithm> // For std::copy() and std::fill().
const size_t _sz = [whatever];
// Simple way of setting size, but not the best. See below.
class Node
{
bool _isBase;
std::array<int, _sz> _channelList;
public:
Node(bool isBase, const int (&channelList)[_sz]);
Node(bool isBase, const std::array<int, _sz>& channelList);
// ...
};
/* Alternatively, you can code the size into the class as a magic number (not a good idea),
* or better yet, make it a template parameter for the class:
* template<size_t _sz> class Node
* {
* bool _isBase;
* std::array<int, _sz> _channelList;
*
* public:
* Node(/ * parameters * /);
*
* template<size_t N>
* Node(/ * parameters * /);
* // ...
* };
* When using the class, you would declare an instance as "Node<SIZE>", where "SIZE" is the
* desired size.
*
* Note that if you make the size a template parameter, and define the member functions
* outside of the class' definition, you have to put the same template at the start of each
* member function:
* template<size_t _sz> Node<_sz>::Node(/ * parameters * /)
* {
* // ...
* }
* This also applies to templated member functions, which will have two sets of template
* parameters.
* template<size_t _sz> template<size_t N> Node<_sz>::Node(/ * parameters * /)
* {
* // ...
* }
*/
// Constructor initialising from C array, if you need to work with preexisting code.
Node::Node(bool isBase, const int (&channelList)[_sz]) : _isBase(isBase)
{
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
}
// Constructor using std::array.
Node::Node(bool isBase, const std::array<int, _sz>& channelList)
: _isBase(isBase), _channelList(channelList)
{
// Initialisation list handles everything.
}
// Note, however, that this will cause issues if the size of channelList doesn't
// necessarily match the size of _channelList. To solve this, we can change Node as
// follows:
// (Note that delegation requires a C++11-compatible compiler.)
#include <type_traits> // For std::integral_constant, std::true_type, and std::false_type.
class Node {
bool _isBase;
std::array<int, _sz> _channelList;
// Actual constructors (C-style array):
template<size_t N>
Node(std::true_type x, bool isBase, const int (&channelList)[N]);
template<size_t N>
Node(std::false_type x, bool isBase, const int (&channelList)[N]);
// Actual constructors (std::array):
template<size_t N>
Node(std::true_type x, bool isBase, const std::array<int, N>& channelList);
template<size_t N>
Node(std::false_type x, bool isBase, const std::array<int, N>& channelList);
public:
// Public constructors, delegate to one of the actual constructors.
// C-style array:
template<size_t N>
Node(bool isBase, const int (&channelList)[N]);
// std::array:
template<size_t N>
Node(bool isBase, const std::array<int, N>& channelList);
// ...
};
/* Now, these constructors are easy enough to make.
* I'll provide an example using C-style arrays. To make versions that take a
* std::array instead, change the parameter:
* const int (&channelList)[N]
* to:
* const std::array<int, N>& channelList
* The constructors should work properly with either kind of array.
*/
// Check if passed array is smaller than or equal to _sz, or if it's larger..
template<size_t N> Node::Node(bool isBase, const int (&channelList)[N])
: Node(std::integral_constant<bool, N <= _sz>{}, isBase, channelList) { }
// N is smaller than or equal to _sz.
template<size_t N> Node::Node(std::true_type x, bool isBase, const int (&channelList)[N])
: _isBase(isBase)
{
// Copy channelList into _channelList.
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
// Fill empty space at the end of _channelList.
std::fill(&(_channelList[N]), std::end(_channelList), '\0');
}
// N is larger than _sz.
template<size_t N> Node::Node(std::false_type x, bool isBase, const int (&channelList)[N])
{
// Handle appropriately.
}
This should allow you to get the functionality you want. [Note that you can also use the above delegation, true_type, and false_type constructors to fill C-style arrays as well as std::arrays, if you need to use them.]
If the size isn't fixed:
#include <vector>
#include <algorithm>
class Node {
bool _isBase;
std::vector<int> _channelList;
public:
template<size_t N>
Node(bool isBase, const int (&channelList)[N]);
// ...
};
template<size_t N> Node::Node(bool isBase, const int (&channelList)[N]) : _isBase(isBase)
{
_channelList.assign(std::begin(channelList), std::end(channelList));
}
// You can also define a constructor that takes a std::array<int, N>, if you
// so desire. Again, the only change needed is the parameter itself.
As a vector's length can be changed at runtime, we can use vector::assign to allocate space and store the entirety of channelList.
Regardless of whether _channelList is stored as a C array, std::array, or std::vector, it's relatively easy to define getters and setters.
Getter (entire thing):
// All of the following use this class definition, with comments identifying which
// functions use which parts.
// Note that the trailing "const" in each getter signature indicates that the function
// cannot be used to modify the instance. It's optional, but useful to include.
class Node {
// Return C array (either way).
int _channelListC[_sz];
// Return std::array.
std::array<int, _sz> _channelListSArr;
// Return std::vector.
std::vector<int> _channelListSVec;
// Return C array the readable way.
typedef int _channelListC_t[_sz];
// C++11 alternative typedef:
using _channelListC_t = decltype(_channelList);
// The C++11 version is safer, as "decltype(_channelList)" won't break if you change
// _channelList's implementation.
// If you need to return the entire array, it may be a good idea to make this a public
// typedef, so it's easier & safer to declare a variable you can return it to.
public:
// Return C array the ugly way.
const int (&getChannelListCUgly() const)[_sz];
// Return C array the readable way.
const _channelListC_t& getChannelListCReadable() const;
// Return C array the readable C++11 way.
auto getChannelListCReadableCPP11() const -> const int(&)[_sz];
// Return std::array.
const std::array<int, _sz>& getChannelListSArr() const;
// Return std::vector.
const std::vector<int>& getChannelListSVec() const;
};
// Return C array:
/* Note that you can't return an array from a function. However, you can return a pointer
* or reference to an array, depending on whether you use * or & in the signature.
*/
// The ugly way:
const int (&Node::getChannelListCUgly() const)[_sz]
{
return _channelList;
}
// The readable way:
const Node::_channelListC_t& Node::getChannelListCReadable() const
{
return _channelList;
}
// The new readable way, as of C++11:
auto getChannelListCReadableCPP11() const -> const int(&)[_sz]
{
return _channelList;
}
// Return std::array:
const std::array<int, _sz>& Node::getChannelListSArr() const
{
return _channelList;
}
// Return std:;vector:
const std::vector<int>& getChannelListSVec() const
{
return _channelList;
}
Note that to my knowledge, a C-style array returned in this manner must be stored in a reference variable.
Node::_channelListC_t& arr = nodeInstance.getChannelListCUgly();
Getter (single element):
// C array or std::array:
int Node::getChannelListArrElement(int index) const
{
if (index < _sz)
{
// index is valid, return element.
return _channelList[index];
}
else
{
// index >= _sz, and is invalid.
// Handle accordingly.
}
}
// std::vector:
int Node::getChannelListVecElement(int index) const
{
if (index < _channelList.size())
{
// index is valid.
return _channelList[index];
}
else
{
// index is invalid.
// Handle accordingly.
}
}
You can define a setter for the entire thing using the constructors above. I would suggest using std::fill() to erase the contents of _channelList first, then copying the new array into _channelList. You can define a setter for single elements using the single-element getter as a basis.
Setter (entire thing):
// Array (either type):
// "CHANNEL_LIST_TYPE[N] channelList" is either "const int (&channelList)[N]" or
// "std::array<int, N>& channelList". Remember to replace it with the correct one in the
// actual code.
// Public function.
template<size_t N>
void Node::setChannelListArr(CHANNEL_LIST_TYPE[N] channelList)
{
setChannelListArr(std::integral_constant<bool, N <= _sz>{}, channelList);
}
// Private function, N <= _sz.
template<size_t N>
void Node::setChannelListArr(std::true_type x, CHANNEL_LIST_TYPE[N] channelList)
{
std::fill(std::begin(_channelList), std::end(_channelList), '\0');
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
}
// Private function, N > _sz.
template<size_t N>
void Node::setChannelListArr(std::false_type x, CHANNEL_LIST_TYPE[N] channelList)
{
// channelList is too large. Handle appropriately.
}
// std::vector:
// "CHANNEL_LIST_TYPE[N]" is used as above, and should be replaced in your actual code.
// Also note that you can easily modify this function to accept another vector, by
// removing the template, making the parameter "const std::vector<int>& channelList", and
// using "channelList.size()" in place of "N" when calling resize().
template<size_t N>
void Node::setChannelListVec(CHANNEL_LIST_TYPE[N] channelList)
{
_channelList.resize(N); // Resize _channelList to have N elements.
std::fill(std::begin(_channelList), std::end(_channelList), '\0');
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
}
Setter (single element):
// Array (either type):
void Node::setChannelListArrElement(int index, int value)
{
if (index < _sz)
{
_channelList[index] = value;
}
else
{
// index is invalid. Handle accordingly.
}
}
// std::vector:
void Node::setChannelListVecElement(int index, int value)
{
if (index < _channelList.size())
{
_channelList[index] = value;
}
else
{
// index is invalid. Handle accordingly.
}
}
// Alternative std::vector setter:
void Node::setChannelListVecElement2(int index, int value)
{
if (index >= _channelList.size())
{
// index is out of bounds. Resize vector to fit it.
_channelList.resize(index + 1, '\0');
}
// Modify element.
_channelList[index] = value;
}
Note that this answer assumes that channelList is null-terminated, as it appears to be. If channelList isn't null-terminated, but you want to stop filling _channelList at the first null element, then you'll have to do a bit more work, likely using your while loop.
You can find working examples of most of the above here. It's a bit of a mess, since it's just a quick program I used to test various things while typing up this answer.
[My apologies for any typoes and/or errors I may have missed. I believe I caught them, but there may still be some there.]
[Edit: Added a note about using fixed-size template constructors with C arrays. Added the C++11 trailing return type way of returning a reference to an array. Added a simple, working example.]
[Edit: Added an additional single-element setter for a vector.]

Related

Use struct member pointer to fill-in a struct in C++

So I have the following available:
struct data_t {
char field1[10];
char field2[20];
char field3[30];
};
const char *getData(const char *key);
const char *field_keys[] = { "key1", "key2", "key3" };
This code is given to my and I cannot modify it in any way. It comes from some old C project.
I need to fill in the struct using the getData function with the different keys, something like the following:
struct data_t my_data;
strncpy(my_data.field1, getData(field_keys[0]), sizeof(my_data.field1));
strncpy(my_data.field1, getData(field_keys[1]), sizeof(my_data.field2));
strncpy(my_data.field1, getData(field_keys[2]), sizeof(my_data.field3));
Of course, this is a simplification, and more things are going on in each assignment. The point is that I would like to represent the mapping between keys and struct member in a constant structure, and use that to transform the last code in a loop. I am looking for something like the following:
struct data_t {
char field1[10];
char field2[20];
char field3[30];
};
typedef char *(data_t:: *my_struct_member);
const std::vector<std::pair<const char *, my_struct_member>> mapping = {
{ "FIRST_KEY" , &my_struct_t::field1},
{ "SECOND_KEY", &my_struct_t::field2},
{ "THIRD_KEY", &my_struct_t::field3},
};
int main()
{
data_t data;
for (auto const& it : mapping) {
strcpy(data.*(it.second), getData(it.first));
// Ideally, I would like to do
// strlcpy(data.*(it.second), getData(it.first), <the right sizeof here>);
}
}
This, however, has two problems:
It does not compile :) But I believe that should be easy to solve.
I am not sure about how to get the sizeof() argument for using strncpy/strlcpy, instead of strcpy. I am using char * as the type of the members, so I am losing the type information about how long each array is. In the other hand, I am not sure how to use the specific char[T] types of each member, because if each struct member pointer has a different type I don't think I will be able to have them in a std::vector<T>.
As explained in my comment, if you can store enough information to process a field in a mapping, then you can write a function that does the same.
Therefore, write a function to do so, using array references to ensure what you do is safe, e.g.:
template <std::size_t N>
void process_field(char (&dest)[N], const char * src)
{
strlcpy(dest, getData(src), N);
// more work with the field...
};
And then simply, instead of your for loop:
process_field(data.field1, "foo");
process_field(data.field2, "bar");
// ...
Note that the amount of lines is the same as with a mapping (one per field), so this is not worse than a mapping solution in terms of repetition.
Now, the advantages:
Easier to understand.
Faster: no memory needed to keep the mapping, more easily optimizable, etc.
Allows you to write different functions for different fields, easily, if needed.
Further, if both of your strings are known at compile-time, you can even do:
template <std::size_t N, std::size_t M>
void process_field(char (&dest)[N], const char (&src)[M])
{
static_assert(N >= M);
std::memcpy(dest, src, M);
// more work with the field...
};
Which will be always safe, e.g.:
process_field(data.field1, "123456789"); // just fits!
process_field(data.field1, "1234567890"); // error
Which has even more pros:
Way faster than any strcpy variant (if the call is done in run-time).
Guaranteed to be safe at compile-time instead of run-time.
A variadic templates based solution:
struct my_struct_t {
char one_field[30];
char another_field[40];
};
template<typename T1, typename T2>
void do_mapping(T1& a, T2& b) {
std::cout << sizeof(b) << std::endl;
strncpy(b, a, sizeof(b));
}
template<typename T1, typename T2, typename... Args>
void do_mapping(T1& a, T2& b, Args&... args) {
do_mapping(a, b);
do_mapping(args...);
}
int main()
{
my_struct_t ms;
do_mapping(
"FIRST_MAPPING", ms.one_field,
"SECOND_MAPPING", ms.another_field
);
return 0;
}
Since data_t is a POD structure, you can use offsetof() for this.
const std::vector<std::pair<const char *, std::size_t>> mapping = {
{ "FIRST_FIELD" , offsetof(data_t, field1},
{ "SECOND_FIELD", offsetof(data_t, field2)}
};
Then the loop would be:
for (auto const& it : mapping) {
strcpy(static_cast<char*>(&data) + it.second, getData(it.first));
}
I don't think there's any way to get the size of the member similarly. You can subtract the offset of the current member from the next member, but this will include padding bytes. You'd also have to special-case the last member, subtracting the offset from the size of the structure itself, since there's no next member.
The mapping can be a function to write the data into the appropriate member
struct mapping_t
{
const char * name;
std::function<void(my_struct_t *, const char *)> write;
};
const std::vector<mapping_t> mapping = {
{ "FIRST_KEY", [](data_t & data, const char * str) { strlcpy(data.field1, str, sizeof(data.field1); } }
{ "SECOND_KEY", [](data_t & data, const char * str) { strlcpy(data.field2, str, sizeof(data.field2); } },
{ "THIRD_KEY", [](data_t & data, const char * str) { strlcpy(data.field3, str, sizeof(data.field3); } },
};
int main()
{
data_t data;
for (auto const& it : mapping) {
it.write(data, getData(it.name));
}
}
To iterate over struct member you need:
offset / pointer to the beginning of that member
size of that member
struct Map {
const char *key;
std::size_t offset;
std::size_t size;
};
std::vector<Map> map = {
{ field_keys[0], offsetof(data_t, field1), sizeof(data_t::field1), },
{ field_keys[1], offsetof(data_t, field2), sizeof(data_t::field2), },
{ field_keys[2], offsetof(data_t, field3), sizeof(data_t::field3), },
};
once we have that we need strlcpy:
std::size_t mystrlcpy(char *to, const char *from, std::size_t max)
{
char * const to0 = to;
if (max == 0)
return 0;
while (--max != 0 && *from) {
*to++ = *from++;
}
*to = '\0';
return to0 - to - 1;
}
After having that, we can just:
data_t data;
for (auto const& it : map) {
mystrlcpy(reinterpret_cast<char*>(&data) + it.offset, getData(it.key), it.size);
}
That reinterpret_cast looks a bit ugly, but it just shift &data pointer to the needed field.
We can also create a smarter container which takes variable pointer on construction, thus is bind with an existing variable and it needs a little bit of writing:
struct Map2 {
static constexpr std::size_t max = sizeof(field_keys)/sizeof(*field_keys);
Map2(data_t* pnt) : mpnt(pnt) {}
char* getDest(std::size_t num) {
std::array<char*, max> arr = {
mpnt->field1,
mpnt->field2,
mpnt->field3,
};
return arr[num];
}
const char* getKey(std::size_t num) {
return field_keys[num];
}
std::size_t getSize(std::size_t num) {
std::array<std::size_t, max> arr = {
sizeof(mpnt->field1),
sizeof(mpnt->field2),
sizeof(mpnt->field3),
};
return arr[num];
}
private:
data_t* mpnt;
};
But probably makes the iterating more readable:
Map2 m(&data);
for (std::size_t i = 0; i < m.max; ++i) {
mystrlcpy(m.getDest(i), getData(m.getKey(i)), m.getSize(i));
}
Live code available at onlinegdb.

c++ send arguments to union (variable union)

well i cant find how do this, basically its a variable union with params, basic idea, (writed as function)
Ex1
union Some (int le)
{
int i[le];
float f[le];
};
Ex2
union Some
{
int le;
int i[le];
float f[le];
};
obs this don't works D:
maybe a way to use an internal variable to set the lenght but don't works too.
Thx.
No, this is not possible: le would need to be known at compile-time.
One solution would be to use a templated union:
template <int N> union Some
{
int i[N];
float f[N];
};
N, of course, is compile-time evaluable.
Another solution is the arguably more succinct
typedef std::vector<std::pair<int, float>> Some;
or a similar solution based on std::array.
Depending on your use case you could try to simulate a union.
struct Some
{
//Order is important
private:
char* pData;
public:
int* const i;
float* const f;
public:
Some(size_t len)
:pData(new char[sizeof(int) < sizeof(float) ? sizeof(float) : sizeof(int)])
,i ((int*)pData)
,f ((float*)pData)
{
}
~Some()
{
delete[] pData;
}
Some(const Some&) = delete;
Some& operator=(const Some&) = delete;
};
Alternative solution using templates, unique_ptr and explicit casts:
//max_size_of<>: a recursive template struct to evaluate the
// maximum value of the sizeof function of all types passed as
// parameter
//The recursion is done by using the "value" of another
// specialization of max_size_of<> with less parameter types
template <typename T, typename...Args>
struct max_size_of
{
static const std::size_t value = std::max(sizeof(T), max_size_of<Args...>::value);
};
//Specialication for max_size_of<> as recursion stop
template <typename T>
struct max_size_of<T>
{
static const std::size_t value = sizeof(T);
};
//dataptr_auto_cast<>: a recursive template struct that
// introduces a virtual function "char* const data_ptr()"
// and an additional explicit cast operator for a pointer
// of the first type. Due to the recursion a cast operator
// for every type passed to the struct is created.
//Attention: types are not allowed to be duplicate
//The recursion is done by inheriting from of another
// specialization of dataptr_auto_cast<> with less parameter types
template <typename T, typename...Args>
struct dataptr_auto_cast : public dataptr_auto_cast<Args...>
{
virtual char* const data_ptr() const = 0; //This is needed by the cast operator
explicit operator T* const() const { return (T*)data_ptr(); } //make it explicit to avoid unwanted side effects (manual cast needed)
};
//Specialization of dataptr_auto_cast<> as recursion stop
template <typename T>
struct dataptr_auto_cast<T>
{
virtual char* const data_ptr() const = 0;
explicit operator T* const() const { return (T*)data_ptr(); }
};
//union_array<>: inherits from dataptr_auto_cast<> with the same
// template parameters. Also has a static const member "blockSize"
// that indicates the size of the largest datatype passed as parameter
// "blockSize" is used to determine the space needed to store "size"
// elements.
template <typename...Args>
struct union_array : public dataptr_auto_cast<Args...>
{
static const size_t blockSize = max_size_of<Args...>::value;
private:
std::unique_ptr<char[]> m_pData; //std::unique_ptr automatically deletes the memory it points to on destruction
size_t m_size; //The size/no. of elements
public:
//Create a new array to store "size" elements
union_array(size_t size)
:m_pData(new char[size*blockSize])
,m_size(size)
{
}
//Copy constructor
union_array(const union_array<Args...>& other)
:m_pData(new char[other.m_size*blockSize])
,m_size(other.m_size)
{
memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
}
//Move constructor
union_array(union_array<Args...>&& other)
:m_pData(std::move(other.m_pData))
,m_size(std::move(other.m_size))
{
}
union_array& operator=(const union_array<Args...>& other)
{
m_pData = new char[other.m_size*blockSize];
m_size = other.m_size;
memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
}
union_array& operator=(union_array<Args...>&& other)
{
m_pData = std::move(other.m_pData);
m_size = std::move(other.m_size);
}
~union_array() = default;
size_t size() const
{
return m_size;
}
//Implementation of dataptr_auto_cast<>::data_ptr
virtual char* const data_ptr() const override
{
return m_pData.get();
}
};
int main()
{
auto a = union_array<int, char, float, double>(5); //Create a new union_array object with enough space to store either 5 int, 5 char, 5 float or 5 double values.
((int*)a)[3] = 3; //Use as int array
auto b = a; //copy
((int*)b)[3] = 1; //Change a value
auto c = std::move(a);// move a to c, a is invalid beyond this point
// std::cout << ((int*)a)[3] << std::endl; //This will crash as a is invalid due to the move
std::cout << ((int*)b)[3] << std::endl; //prints "1"
std::cout << ((int*)c)[3] << std::endl; //prints "3"
}
Explanation
template <typename T, typename...Args>
struct max_size_of
{
static const std::size_t value = std::max(sizeof(T), max_size_of<Args...>::value);
};
template <typename T>
struct max_size_of<T>
{
static const std::size_t value = sizeof(T);
};
max_size_of<> is used to get the largest sizeof() value of all types passed as template paremeters.
Let's have a look at the simple case first.
- max_size_of<char>::value: value will be set to sizeof(char).
- max_size_of<int>::value: value will be set to sizeof(int).
- and so on
If you put in more than one type it will evaluate to the maximum of the sizeof of these types.
For 2 types this would look like this: max_size_of<char, int>::value: value will be set to std::max(sizeof(char), max_size_of<int>::value).
As described above max_size_of<int>::value is the same as sizeof(int), so max_size_of<char, int>::value is the same as std::max(sizeof(char), sizeof(int)) which is the same as sizeof(int).
template <typename T, typename...Args>
struct dataptr_auto_cast : public dataptr_auto_cast<Args...>
{
virtual char* const data_ptr() const = 0;
explicit operator T* const() const { return (T*)data_ptr(); }
};
template <typename T>
struct dataptr_auto_cast<T>
{
virtual char* const data_ptr() const = 0;
explicit operator T* const() const { return (T*)data_ptr(); }
};
dataptr_auto_cast<> is what we use as a simple abstract base class.
It forces us to implement a function char* const data_ptr() const in the final class (which will be union_array).
Let's just assume that the class is not abstract and use the simple version dataptr_auto_cast<T>:
The class implements a operator function that returns a pointer of the type of the passed template parameter.
dataptr_auto_cast<int> has a function explicit operator int* const() const;
The function provides access to data provided by the derived class through the data_ptr()function and casts it to type T* const.
The const is so that the pointer isn't altered accidentially and the explicit keyword is used to avoid unwanted implicit casts.
As you can see there are 2 versions of dataptr_auto_cast<>. One with 1 template paremeter (which we just looked at) and one with multiple template paremeters.
The definition is quite similar with the exception that the multiple parameters one inherits dataptr_auto_cast with one (the first) template parameter less.
So dataptr_auto_cast<int, char> has a function explicit operator int* const() const; and inherits dataptr_auto_cast<char> which has a function explicit operator char* const() const;.
As you can see there is one cast operator function implemented with each type you pass.
There is only one exception and that is passing the same template parameter twice.
This would lead in the same operator function being defined twice within the same class which doesn't work.
For this use case, using this as a base class for the union_array, this shouldn't matter.
Now that these two are clear let's look at the actual code for union_array:
template <typename...Args>
struct union_array : public dataptr_auto_cast<Args...>
{
static const size_t blockSize = max_size_of<Args...>::value;
private:
std::unique_ptr<char[]> m_pData;
size_t m_size;
public:
//Create a new array to store "size" elements
union_array(size_t size)
:m_pData(new char[size*blockSize])
,m_size(size)
{
}
//Copy constructor
union_array(const union_array<Args...>& other)
:m_pData(new char[other.m_size*blockSize])
,m_size(other.m_size)
{
memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
}
//Move constructor
union_array(union_array<Args...>&& other)
:m_pData(std::move(other.m_pData))
,m_size(std::move(other.m_size))
{
}
union_array& operator=(const union_array<Args...>& other)
{
m_pData = new char[other.m_size*blockSize];
m_size = other.m_size;
memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
}
union_array& operator=(union_array<Args...>&& other)
{
m_pData = std::move(other.m_pData);
m_size = std::move(other.m_size);
}
~union_array() = default;
size_t size() const
{
return m_size;
}
virtual char* const data_ptr() const override
{
return m_pData.get();
}
};
As you can see union_array<> inherits from dataptr_auto_cast<> using the same template arguments.
So this gives us a cast operator for every type passed as template paremeter to union_array<>.
Also at the end of union_array<> you can see that the char* const data_ptr() const function is implemented (the abstract function from dataptr_auto_cast<>).
The next interesting thing to see is static const size_t blockSize which is initilialized with the maximum sizeof value of the template paremeters to union_array<>.
To get this value the max_size_of is used as described above.
The class uses std::unique_ptr<char[]> as data storage, as std::unique_ptr automatically will delete the space for us, once the class is destroyed.
Also std::unique_ptr is capable of move semantics, which is used in the move assign operator function and the move constructor.
A "normal" copy assign operator function and a copy constructor are also included and copy the memory accordingly.
The class has a constructor union_array(size_t size) which takes the number of elements the union_array should be able to hold.
Multiplying this value with blockSize gives us the space needed to store exactly size elements of the largest template type.
Last but not least there is an access method to ask for the size() if needed.
C++ requires that the size of a type be known at compile time.
The size of a block of data need not be known, but all types have known sizes.
There are three ways around it.
I'll ignore the union part for now. Imagine if you wanted:
struct some (int how_many) {
int data[how_many];
};
as the union part adds complexity which can be dealt with separately.
First, instead of storing the data as part of the type, you can store pointers/references/etc to the data.
struct some {
std::vector<int> data;
explicit some( size_t how_many ):data(how_many) {};
some( some&& ) = default;
some& operator=( some&& ) = default;
some( some const& ) = default;
some& operator=( some const& ) = default;
some() = default;
~some() = default;
};
here we store the data in a std::vector -- a dynamic array. We default copy/move/construct/destruct operations (explicitly -- because it makes it clearer), and the right thing happens.
Instead of a vector we can use a unique_ptr:
struct some {
std::unique_ptr<int[]> data;
explicit some( size_t how_many ):data(new int[how_many]) {};
some( some&& ) = default;
some& operator=( some&& ) = default;
some() = default;
~some() = default;
};
this blocks copying of the structure, but the structure goes from being size of 3 pointers to being size of 1 in a typical std implementation. We lose the ability to easily resize after the fact, and copy without writing the code ourselves.
The next approach is to template it.
template<std::size_t N>
struct some {
int data[N];
};
this, however, requires that the size of the structure be known at compile-time, and some<2> and some<3> are 'unrelated types' (barring template pattern matching). So it has downsides.
A final approach is C-like. Here we rely on the fact that data can be variable in size, even if types are not.
struct some {
int data[1]; // or `0` in some compilers as an extension
};
some* make_some( std::size_t n ) {
Assert(n >= 1); // unless we did `data[0]` above
char* buff = new some[(n-1)*sizeof(int) + sizeof(some)]; // note: alignment issues on some platforms?
return new(buff) some(); // placement new
};
where we allocate a buffer for some of variable size. Access to the buffer via data[13] is practically legal, and probably actually so as well.
This technique is used in C to create structures of variable size.
For the union part, you'll want to create a buffer of char with the right size std::max(sizeof(float), sizeof(int))*N, and expose functions:
char* data(); // returns a pointer to the start of the buffer
int* i() { return reinterpret_cast<int*>(data()); }
float* f() { return reinterpret_cast<float*>(data()); }
you may also need to properly initialize the data as the proper type; in theory, a char buffer of '\0's may not correspond to defined float values or ints that are zero.
I would like to suggest a different approach: Instead of tying the number of elements to the union, tie it outside:
union Some
{
int i;
float f;
};
Some *get_Some(int le) { return new Some[le]; }
Don't forget to delete[] the return value of get_Some... Or use smart pointers:
std::unique_ptr<Some[]> get_Some(int le)
{ return std::make_unique<Some[]>(le); }
You can even create a Some_Manager:
struct Some_Manager
{
union Some
{
int i;
float f;
};
Some_Manager(int le) :
m_le{le},
m_some{std::make_unique<Some[]>(le)}
{}
// ... getters and setters...
int count() const { return m_le; }
Some &operator[](int le) { return m_some[le]; }
private:
int m_le{};
std::unique_ptr<Some[]> m_some;
};
Take a look at the Live example.
It's not possible to declare a structure with dynamic sizes as you are trying to do, the size must be specified at run time or you will have to use higher-level abstractions to manage a dynamic pool of memory at run time.
Also, in your second example, you include le in the union. If what you were trying to do were possible, it would cause le to overlap with the first value of i and f.
As was mentioned before, you could do this with templating if the size is known at compile time:
#include <cstdlib>
template<size_t Sz>
union U {
int i[Sz];
float f[Sz];
};
int main() {
U<30> u;
u.i[0] = 0;
u.f[1] = 1.0;
}
http://ideone.com/gG9egD
If you want dynamic size, you're beginning to reach the realm where it would be better to use something like std::vector.
#include <vector>
#include <iostream>
union U {
int i;
float f;
};
int main() {
std::vector<U> vec;
vec.resize(32);
vec[0].i = 0;
vec[1].f = 42.0;
// But there is no way to tell whether a given element is
// supposed to be an int or a float:
// vec[1] was populated via the 'f' option of the union:
std::cout << "vec[1].i = " << vec[1].i << '\n';
}
http://ideone.com/gjTCuZ

End of array points to newly allocated array C++

Using C++, I am trying to create an array that holds pointers to objects I'm storing. But when the array is full, I want to expand the array.
the easy option is to allocate a new array with bigger size, then copy the elements to it, this is quite inefficient, and I thought of another way I want to try to do it:
create array of fixed size X
When full, create a new array, and make the end of the first array point to the start of the first element
Repeat as long as needed
What methods can I use to do that? I thought of one way to do it, but it seems very hacky:
declare all my new array as pointers to object pointer, then reinterprit_cast the filled elements to object pointer.
Note: I know I can use Vector, but I am instructed not to use std library.
Kind Regards,
There are some good answers in the comments already. I just want to provide a way to achieve exactly the behavior you described.
Since the elements of the array are pointers as well, you can define a union as the element of your array like this:
template<typename T>
union Cell
{
T* pElm;
Cell* pNext;//A fixed size array of Cells
}
And then build your array on top of it. For example:
template<typename T>
class SpecialArray
{
public:
//the next pointer is included
static const size_t ARRAY_LEN = 1000;// For example
using Pointer = T*;
using Segment = Cell<T>[ARRAY_LEN];
protected:
Segment* pFirst;
size_t mSize;
public:
SpecialArray()
:pFirst(nullptr),mSize(0){}
SpecialArray(SpecialArray&&){}
~SpecialArray(){}
Pointer& operator[](size_t index)
{
Segment* seg = pFirst;
size_t offest = 0;
//Search logic...
return seg[offest]->pElm;
}
const Pointer& operator[](size_t index) const;
};
Using C++, I am trying to create an array that holds pointers to
objects I'm storing. But when the array is full, I want to expand the
array.
With C++ templates and C primitives we can improvise a simple vector like below. And the grow buffer strategy is to double the size when the threshold is met.
#include <iostream>
#include <stdlib.h>
template <typename T>
class MyVector
{
public:
MyVector() : m_count(0), m_size(0), m_buffer(0)
{
m_size = bufInitSize;
m_buffer = (T*)malloc(sizeof(T) * bufInitSize);
}
~MyVector()
{
if (m_buffer)
free(m_buffer);
}
void add(const T& p)
{
if (m_count + 1 >= m_size)
{
m_size *= 2;
m_buffer = (T*)realloc(m_buffer, sizeof(T) * m_size);
}
m_buffer[m_count ++ ] = p;
}
T& operator[](int idx)
{
return m_buffer[idx];
}
private:
static const int bufInitSize = 1024;
T* m_buffer;
int m_count;
int m_size;
};
void main()
{
// using MyVector
MyVector<int*> vctOfIntPtr;
int n = 100;
vctOfIntPtr.add(&n);
int* pN = vctOfIntPtr[0];
std::cout << *pN;
}

Return one dimension of an array?

How would I only return one dimension of an array, while ignoring the other?
Such as:
int map[4][8];
int MapManager::getMapX(int x)
{
return map[x];
}
The return type should be int* or int[], not int:
int* MapManager::getMapX(int x) {
return map[x];
}
Other than that, you are fine.
With a 2 dimensional array you will only be able to directly return a one dimensional array for the inner array by converting to a pointer, such as
int map[4][8];
int* MapManager::getMapX(int x)
{
return map[x];
}
For the other 'dimension', you will need to have another external array to copy to:
int* MapManager::getMapY(int y, int *outArray, int numElements)
{
for(int i = 0; i < numElements; i++) {
outArray[i] = map[i][y];
}
}
This array needs to be allocated with the correct size. (8 in this case).
The reason for this is that the array elements for the y 'column' are not contiguous in memory, but spread across several arrays (which are the 'x' rows). C arrays rely on this contiguous concept for accessing the elements.
Since none of the other answers here return an actual array (pointers are not arrays), I thought I might show how to really return an array. Or the closest thing possible, which is a reference to an array.
typedef int row_type[8];
row_type& MapManager::getMapX(int x) {
return map[x];
}
What's the point of this? The pointer works the same!
No, it doesn't. The pointer loses type information, namely, the size. You can make begin() and end() functions work with arrays, but you can't with pointers:
// pointer version
int* MapManager::getMapX_ptr(int x) {
return map[x];
}
row_type& row = getMapX(0);
// row is of type int(&)[8]
// notice the size is not lost in the type!
std::sort(begin(row), end(row));
// compiles fine! end() correctly finds the end of the row
int* ptr = getMapX_ptr(0);
// notice how ptr has no size information at all
std::sort(begin(ptr), end(ptr));
// can't possible be made to work!
You can't write end for int*.
template <typename T, std::size_t N>
T* end(T(&arr)[N]) {
return &arr[0] + N;
}
template <typename T>
T* end(T* ptr) {
// what here?
// guess?
// pick a random number?
}
You could create a special class that provides a strided view of an array. That is to say, it skips over values. Here's the beginnings of something like that:
template<typename T>
class StridedView
{
public:
StridedView(T * b, int stride, int count)
:begin_(b), stride_(stride), count_(count)
{}
template<int N>
StridedView(T (&arr)[N])
:begin_(arr), stride_(1), count_(N)
{}
T & operator[](int index)
{
return *(begin_ + index * stride_);
}
const T & operator[](int index) const
{
return *(begin_ + index * stride_);
}
int size() const { return count_; }
private:
T * begin_;
int stride_;
int count_;
};
Then you could have functions that can get you a row or a column as appropriate:
template<typename T, int R, int C>
StridedView<T> GetRow(T (&arr)[R][C], int row)
{
T * begin = (*arr) + (row * C);
return StridedView<T>(begin, 1, C);
}
template<typename T, int R, int C>
StridedView<T> GetColumn(T (&arr)[R][C], int column)
{
T * begin = (*arr) + column;
return StridedView<T>(begin, C, R);
}
Demo

Using new with fixed length array typedef

How do I define a typedef for a fixed length array so that I can also 'new'. The following does not work:
typedef double Vector[3];
Vector *v = new Vector; // does not compile
We are trying to wrap into C++ some old C code which handles float * and float (*)[3] in a generic way.
The pointer to an double[3] is double * - so this will work:
typedef double Vector[3];
double *v = new Vector;
But I suggest you don't use it that way - to delete the array you need the array-delete-operator:
delete[] v;
But on new Vector you don't see it is an array and so it might be forgotten.
This case is handled (and strongly recommended to avoid) in Scott Meyers Effective C++. So better don't use an typedef here.
class Vector
{
public: // methods
double * data() { return mData; }
const double * data() const { return mData; }
double & operator[](int i) { return mData[i]; }
double operator[](int i) const { return mData[i]; }
private: // attributes
double mData[3];
};
will allow
Vector * pv = new Vector;
Vector & v = *pv;
v[0] = 1;
v[1] = 2;
v[2] = 3;
pass_it_to_legacy_lib(v.data());
delete pv;
One issue with your original example is that it would invoke the new operator where the new[] would actually be correct. Also, it would make it non-obvious that delete[] had to be used instead of plain delete.
The class approach doesn't need new[] and takes full advantage of the apriori fixed length.
If you're happy to use templates in your C++ code, something like this could work..
template <typename T, int S>
struct array
{
array() : _inst() {}
template<typename _F>
void operator()(_F & f)
{
f(_inst);
}
operator T*() { return _inst; }
// real array
T _inst[S];
};
typedef array<double, 4> d4;
void foo(double*)
{
}
int main(void)
{
d4 d; // no need for new, but you can use if you want
// first way to call is to pass the function to the array object, which will then
// visit
d(foo);
// take advantage of the type operator (operator T*)
foo(d);
}
#include <cassert>
#include <vector>
using namespace std;
template<typename Type, int Dimension>
const vector<Type> make_fixed_vector(const Type& value = Type())
{
return vector<Type>(Dimension, value);
}
int main(void)
{
vector<int> v3 = make_fixed_vector<int, 3>();
assert(v3.size() == 3);
}
C++1x compilers are able to deduce the type of a variable, which is handy when declaring multi-dimensional "fixed" vectors using this technique:
.
.
.
template<typename Type, int Rows, int Columns>
const vector<vector<Type> > make_fixed_vector_vector(const Type& value = Type())
{
return vector<vector<Type> >(Rows, make_fixed_vector<Type, Columns>(value));
}
int main(void)
{
auto vv = make_fixed_vector_vector<int, 3, 4>(42);
assert(vv.size() == 3);
assert(vv[0].size() == 4);
assert(vv[0][0] == 42);
assert(vv[2][3] == 42);
}
I had this simple idea when programming a parser-function for list expressions which shall return a fixed-size vector of vector of integers. For example, a vector<vector<int> >(1) for a expression like "(0,8)", but a vector<vector<int> >(2) for a expression like "(3-4)(5)" and so on. In the application up to 5 parenthesized definitions are possible, which represent logical references to program data. I first try to parse a vector<vector<int> >(5). Worked? Ok, got reference type A, the most detailed one. Otherwise vector<vector<int> >(4) indicates a reference type B etc.
For this purpose make_fixed_vector worked well, but from a general perspective the technique has flaws. Most notably, since make_fixed_vector returns no true type, its dimension(s) cannot be checked at compile-time. At runtime reserve, resize and push_back calls are possible. And, since function templates cannot have default template arguments, custom allocators require more typing:
template<typename Type, int Dimension, template<typename> class Allocator>
const vector<Type Allocator<Type> > make_fixed_vector(const Type& value = Type())
{
return vector<Type, Allocator<Type> >(Dimension, value);
}
vector<int> v3 = make_fixed_vector<int, 3, std::allocator>();
etc. etc. But this technique keeps smaller projects basic. Unless this virtue is relevant Boost's boost::array might be more realistic.