Moin Moin,
I'd like to pass a variable Pointer Address to a Function to get the bytes of the variable in a buffer - see code snippet!
This version of the code works ... but I like to skip the additional address definition before the function call.
Thank you very much!
float f = 133.2342;
int x = 12345;
byte buffer[8] = {0,0,0,0,0,0,0,0};
void var_to_byte(const int anz, int* var_split, int* buffer)
{
for (int i=0;i<8;i++) buffer[i]=0;
for (int i=0;i<anz;i++)
{
buffer[i]=var_split[i];
}
}
void setup()
{
Serial.begin(115200);
// I like to skip these lines
int z = &f;
int y = &buffer;
// call the function with var_to_byte(sizeof(f),&f,&buffer); is not working
var_to_byte ( sizeof(f),z,y); // this works !!!!
Serial.print("Buffer: ");
for (int i=0;i<8;i++)
{
if (buffer[i]<0x10)
{
Serial.print("0");
}
Serial.print(buffer[i],HEX);
}
Serial.println();
}
void loop() {
// put your main code here, to run repeatedly:
}
I don't know how to solve this problem ... and yes I'm new to C++ and/or Arduino ....
It cannot work because the types do not match in the slightest.
You can serialise a value into an existing buffer like so:
// if out is too small, this will blow up (undefined behaviour)
inline void serialise(float const f, std::byte* out) {
auto const* raw = reinterpret_cast<std::byte const*>(&f);
std::copy(raw, raw+sizeof(float), out);
}
But you can also avoid having to pass in the buffer:
(see below if you want to avoid std::array)
#include <array>
inline auto serialise(float const f) {
std::array<std::byte, sizeof(f)> out;
auto const* raw = reinterpret_cast<std::byte const*>(&f);
std::copy(raw, raw+sizeof(f), out.begin());
return out; // copy elided by all sensible compilers
}
// usage
int main() {
float const f = 3.14f;
auto const ser = serialise(f);
// do stuff with ser
for (auto const x: ser) {
if (x<0x10)
Serial.print("0");
Serial.print(x,HEX);
}
Serial.println();
}
Of course, you can generalise serialise to any type:
template <typename T>
inline auto serialise(T const& t) {
// pretty much the same code only with T instead of float
}
Edit:
If you have to avoid using the standard library, you can also pass a sized raw-array reference:
auto serialise(float const f, std::byte (&out)[sizeof(float)]) {
auto const* raw = reinterpret_cast<std::byte const*>(&f);
std::copy(raw, raw+sizeof(f), out);
return out; // copy elided by all sensible compilers
}
The types in your code don't make sense. If you want to populate a buffer of bytes then why does your function say this int* buffer? That's a buffer of int not byte.
The simple approach is to use the standard library function memcpy for copying bytes. No need for your own function for this standard task.
memcpy(buffer, &f, sizeof f); // copy the bytes of f to buffer
memcpy is declared in the header file <string.h>
Related
Here is my code:
#include <string.h>
#include <stdlib.h>
template <int ...I>
class MetaString
{
char buffer_[sizeof...(I)+1];
public:
// A constexpr constructor
constexpr MetaString(const char * arg) :buffer_{ encrypt(arg[I])... }
{}
constexpr const char *get()const { return buffer_; }
private:
constexpr char encrypt(const char c) const { return c ^ 0x55; }
};
char *decrypt(const char* buffer_, int size)
{
char* tmp = (char *)malloc(size + 1);
strcpy_s(tmp, size + 10, buffer_);
for (int i = 0; i < size; i++)
{
*(tmp + i) = *(tmp + i) ^ 0x55;
}
return tmp;
}
int main()
{
constexpr MetaString<0,1,2,3,5> var("Post Malone");
char * var1 = decrypt(var.get(), 5);
std::cout << var1 << std::endl;
return 1;
}
The idea is simple, I create object of MetaString and provide some string to it. The constructor encrypts the argument by XOR. Then I have decrypt function which decrypts value back.
The problem is that I set breakpoint in constructor (specifically this line constexpr MetaString(const char * arg) :buffer_{ encrypt(arg[I])... }) and it is hit when I run in debugging mode. Which as I understand means that the constructor is called during runtime.
To guarantee that functions be evaluated at compile time I created object this way constexpr MetaString<0,1,2,3,5> var("Post Malone"); But I've read that constexpr variable must be literal type.
So my question is how can I manage to have variable like var (which would have encrypted data in it and be evaluated at compilation time) and then call decrypt at runtime and get original value?
constexpr only guarantees that a function or variable can be used in a constant expression. It does not guarantee that a function/object is going to always be evaluated/constructed at compiletime. In your particular case, that's actually not really possible since we're talking about an object with automatic storage duration. The object, if it is going to be created, can only really be created when the program is running. Try making your variable static…
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.
I'm writing a message comuniction lib, and need to read some data to a struct, and append this struct to a vector, and read again, append again.
If in C language, memcpy works perfect, but I want is to make all code in C++11 code style.
I tried to use std::copy, but it needs a begin and end interator, so how exactly I can use std::copy like std::copy(&a, &a + sizeof(A), back_inserter(buffer));?
You can do this:
struct MyStruct {
int a;
double b;
int c;
};
std::vector<uint8_t> buffer;
MyStruct data { 42, 3.14159, 123 };
uint8_t* ptr = reinterpret_cast<uint8_t*>(&data);
std::copy(ptr, ptr + sizeof(data), back_inserter(buffer));
Note that std::copy in this case just reverts to std::memcpy underneath, and reinterpret_cast throws away all type safety of the language. Alexander's suggestion of using a static_assert is a good one.
EDIT:
Mário is right, back_inserter would cause std::copy to not be equivalent to std::memcpy. An alternative could be to reallocate your buffer first, then copy:
size_t len = buffer.size();
buffer.resize(len+sizeof(data));
std::copy(ptr, ptr + sizeof(data), buffer.data() + len);
(or something to that extent).
Here is a clean C++ way to do it:
First a simple range type:
template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
std::size_t size() const { return end()-begin(); }
};
template<class It>
range_t<It> range(It s, It f) { return {s,f}; }
it represents a range of some iterators.
Next, some functions to treat pod data as bytes:
template<class T>
range_t< unsigned char* > as_bytes( T* t ) {
static_assert( std::is_trivially_copyable<T>::value, "bad idea if not trivially copyable" );
auto* ptr = reinterpret_cast<unsigned char*>(t);
return range(ptr, ptr+sizeof(T));
}
template<class T>
range_t< unsigned char const* > as_bytes( T const* t ) {
static_assert( std::is_trivially_copyable<T>::value, "bad idea if not trivially copyable" );
auto* ptr = reinterpret_cast<unsigned char const*>(t);
return range(ptr, ptr+sizeof(T));
}
both read and write versions.
Next, functions that take a structure and stuff them into a vector, or pop them out:
template<class T>
void push_bytes_in( std::vector<std::uint8_t>& target, T const* data ) {
auto bytes = as_bytes(data);
target.insert( target.end(), bytes.begin(), bytes.end() );
}
template<class T>
bool pop_bytes_out( std::vector<std::uint8_t>& src, T* data ) {
auto bytes = as_bytes(data);
if (bytes.size() > src.size()) return false;
std::copy( src.end()-bytes.size(), src.end(), bytes.begin() );
src.resize( src.size()-bytes.size() );
return true;
}
Finally, test code:
struct some_data {
int x, y;
char buff[1024];
};
std::vector<std::uint8_t> bytes;
some_data data{1,2, "hello"};
push_bytes_in( bytes, &data );
some_data d2;
if (!pop_bytes_out( bytes, &d2)) {
std::cout << "failed\n";
return -1;
}
std::cout << d2.buff << "\n";
Live example.
We could optimize push bytes if they turn out to be too slow to pre-size the buffer, then shove the bytes in using std copy or memcpy. However, you should be careful to ensure exponential data reservation in that case.
template<class T>
void push_bytes_in( std::vector<std::uint8_t>& target, T const* data ) {
if (target.capacity() < target.size()+sizeof(T)) {
target.reserve( target.capacity()*3/2 +1 );
}
auto bytes = as_bytes(data);
target.resize( target.size() + sizeof(T) );
std::copy( bytes.begin(), bytes.end(), target.end()-sizeof(T) );
}
that may turn out to be a bit faster.
You can use vector insert member function.
This is better than copy since vector insert knows how to allocate memory(you do not need to use ugly back_inserter).
void append(std::vector<unsigned char>& v, const MyStruct& s){
v.insert(v.end(), (unsigned char*) &s, ((unsigned char*)&s)+sizeof s);
}
full code here
note that this is very simple code compared to Yakk answer but I think it may be easier for some people to read code without templates. Also I use C style cast that some people think should not be done in C++ but I find reinterpret cast too verbose for this use case.
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.]
im pretty new in c++ and my problem is the following:
i need an array in which i want so save values. all valus are of the same type.
there are two cases: the array should save int values or float.
when i compile, i dont knwo yet which type it will be, so it has to be defined while executing the programm.
i tried something like this:
void* myArray;
int a = 10;
if(something){
myArray = new int[a];
}
else{
myArray = new float[a];
}
after this i want so calculate things with these values, but i always get errors because the array is still void
There are several ways of doing this in C++:
You could use a void* and add reinterpret_cast<...> as needed,
You could make an array of unions that have both an int and a float, or
You could use templates.
The first two approaches are idiomatic to C, but not to C++. Both approaches are workable, but they result in solutions that are hard to understand and maintain.
The third approach lets you do things very cleanly:
template <typename T>
void calc() {
// You could use std::vector<T> here for even better flexibility
T* a = new T[10];
... // Perform your computations here
delete[] a;
// You don't need a delete if you use std::vector<T>
}
int main() {
...
// You can make a type decision at runtime
if (mustUseInt) {
calc<int>();
} else {
calc<float>();
}
return 0;
}
struct calculator : public boost::static_visitor<> {
void operator()(const std::vector<int>& vi) const {
// evaluate the array as ints
}
void operator()(const std::vector<float>& vf) const {
// evaluate the array as floats
}
};
using nasty_array = boost::variant<std::vector<int>, std::vector<float>>;
std::unique_ptr<nasty_array> myArray;
int a = 10;
if (something) {
myArray.reset(std::vector<int>(a));
}
else {
myArray.reset(std::vector<float>(a));
}
boost::apply_visitor( calculator(), *myArray );