Here are a couple of function declarations that I'm having trouble understanding how to complete. I've scanned the web to see what a void pointer is, and I understand that it must be cast to something to be useful (because it just points to some memory block), but I don't see how that helps in completing these declarations.
/* type of comparison function that specifies a unique order.
Returns 1 if pointer1 should come before,
0 if equal to, or -1 if after pointer2. */
typedef int (*compare_function) (const void* pointer1, const void* pointer2);
/* get the data object */
void* get_obj(const void* item_pointer);
There are more functions like this, but I think if I understand how to do these two I should be in good shape. For example, for the second function, how do we cast the item_pointer to anything appropriate that should be returned?
void * usually means that you are only interested in the address of the data regardless of its type, some of the reasons:
the internal representation of the data this void * pointing to is hidden, you are not supposed to access the data directly, information hiding, your function 2 is properly an example of this case.
the type is known by some function in the call chain, like with qsort and most functions that pass arguments to other functions.
the type is not required because the data the pointer is pointing to will be handled as different type, like with memcpy which may handle the data as bytes, unsigned char *.
Sorting in C with quicksort uses void pointers so that we can sort any data in arrays. The sort function must return -1, +1, or 0 if the parameter b is before, after or the same as parameter a
#include <stdio.h>
#include <stdlib.h>
int sort_order( const void *, const void *);
int main(void)
{
int i;
char alfa[6] = { ’C’, ’E’, ’A’, ’D’, ’F’, ’B’ };
qsort( (char*)alfa, 6, sizeof(char), sort_order);
for (i=0 ; i<5 ; i++) // now in order?
printf("\nchar %d = %c",i, alfa[i]);
printf("\n");
system("PAUSE");
return 0;
}
int sort_order( const void* a, const void* b)
{
if ( *((char*)a) < *((char*)b) ) return -1 ;
else if ( *((char*)a) > *((char*)b) ) return 1 ;
else return 0 ;
}
Then you can sort your own datatypes:
typedef struct { float left; float right;} ears;
typedef struct{ char name[13]; int weight; ears eararea;} monkey;
monkey* Index[4];
for(i=0;i<4;i++)
Index[i]= (monkey* )malloc(sizeof(monkey));
qsort((void* ) Index, 4, sizeof(monkey* ), sort_order);
// Sorted by weight
int sort_order( const void* a, const void* b) {
if((**((monkey** )a)).weight < (**((monkey** )b)).weight) return -1 ;
else if ((**((monkey** )a)).weight > (**((monkey** )b)).weight ) return 1 ;
else return 0 ;
}
Complete program
#include <stdio.h>
#include <stdlib.h>
typedef struct {
float left;
float right;
} ears;
typedef struct {
char name[13];
int weight;
ears eararea;
} monkey;
int sort_monkeys( const void *, const void *);
int main(void)
{ monkey* monkeys[4];
int i;
for(i=0; i<4; i++) {
monkeys[i]= (monkey* )malloc(sizeof(monkey));
monkeys[i]->weight=i*10;
if (i==2)
monkeys[i]->weight=1;
}
for (i=0 ; i<4; i++)
printf("\nchar %d = %i",i, monkeys[i]->weight);
qsort((void* ) monkeys, 4, sizeof(monkey* ), sort_monkeys);
for (i=0 ; i<4; i++) // now in order?
printf("\nmonkey %d = %i",i, monkeys[i]->weight);
return 0;
}
// Sorted by weight
int sort_monkeys( const void* a, const void* b) {
if((**((monkey** )a)).weight < (**((monkey** )b)).weight) return -1 ;
else if ((**((monkey** )a)).weight > (**((monkey** )b)).weight ) return 1 ;
else return 0 ;
}
Any pointer type may be assigned to a void*, this is useful in cases where a function does not need to know the type, or the type information is conveyed by other means. This allows you to write just one function to deal with any pointer type rather than a separate function for each data type.
While you cannot dereference a void* you can cast it to any type and dereference it - the semantics of that - i.e. whether it is meaningful, depends on the code and is not enforced byte compiler.
Frequently a generic function is not interested in the content of some block of data, just its address and often its size.
As a simple example:
void memcopy( void* to, void* from, int length )
{
char* source = (char*)from ;
char* dest = (char*)to ;
int i ;
for( i = 0; i < lengt; i++ )
{
dest[i] = source[i] ;
}
}
int main()
{
typedef struct
{
int x ;
int y ;
} tItem
tItem AllItems[256] = {0} ;
tItem AllItemsCopy[256] ;
memcopy( AllItemsCopy, AllItems, sizeof(AllItems) ) ;
}
See that memcopy() does not need to know what a tItem is in order to copy an array of them, it only needs to know the addresses and the size on the array in bytes. It casts the void* pointer arguments to reinterpret the data as a char array to perform a byte-by-byte copy. To do that it does not need to know the internal semantics of tItem or any other data object passed to it.
Related
class A{
char info[256];
public:
char* getInfo();
A(char i[256]);
//A.cpp
#include "A.h"
char * A::getInfo(){
return(&info[256]);
}
A::A(char i[256]){
info[256]=i[256];
}
I'm struggling with the accessor. When I try to use getInfo(), I get a char*, and thus with
char test[256] = "test";
FractionException d(test);
for (int i = 0; i < 256; i++) {
cout << d.getInfo()[i] ;
}
I get
╠╠╠╠╠╠╠╠test
I guess I'm doing things the wrong way, but I cant figure it out..
By the way, VScode also warn me on
info[256]=i[256]
by telling me that 257 octets bytes might be written (C6386) but I dont get it ...
Could you help me please ? Thanks !
The problem is, your constructor is not initializing the contents of the info array correctly, and your accessor is returning a bad pointer.
In the constructor, info[256]=i[256] does not do what you think it does. You are trying to copy the 257th element of i into the 257th element of info, which is Undefined Behavior since neither array has 257 elements. That is why the compiler is warning you about it.
Try this instead:
A::A(char i[256]){
for(int x = 0; x < 256; ++x){
info[x] = i[x];
}
}
Alternatively:
#include <algorithm>
A::A(char i[256]){
std::copy_n(i, 256, info);
}
As for the accessor, it is returning a pointer to the non-existent 257th element. You need to return a pointer to the 1st element instead:
char * A::getInfo(){
return(&info[0]);
}
Or simply:
char * A::getInfo(){
return info;
}
This declaration of the constructor
A(char i[256]);
does not make a great sense because the compiler will adjust the parameter declaration like
A(char *i);
Taking into account this code snippet
char test[256] = "test";
FractionException d(test);
for (int i = 0; i < 256; i++) {
cout << d.getInfo()[i] ;
}
It seems you want that the constructor would accept a string. If so then it should be declared like
A( const char * );
and it can be defined like
#include <cstring>
//...
A::A( const char *i ){
strncpy( info, i, sizeof( info ) );
info[sizeof( info ) - 1] = '\0';
}
The member function getInfo should return the array instead of the address of the non-existent element info[256]
char * A::getInfo(){
return info;
}
This method should be also overloaded
const char * getInfo() const;
And this loop
for (int i = 0; i < 256; i++) {
cout << d.getInfo()[i] ;
}
should be substituted for this statement
std::cout << d.getInfo();
I am making a program where I have 2 vectors (clientvec and productslist) and I need to create a 2D boolean matrix where the columns is the size of productslist vector and the lines is the size of clientvec vector, but it gives me this error:
"expression must have a constant value"
Here is the code I used:
unsigned int lines = clientvec.size();
unsigned int columns = productslist.size();
bool matrixPublicity[lines][columns] = {false};
Pls help me..
Edit: I am new at c++ so assume I know nothing xD
Edit2: I already know for the answers that I cannot initialize an array with non constant values, now the question is how can I put them after initialize...
The error message is clear: :expression must have a constant value"
It means the array dimension cannot be of variable type. Only enums or pre-processor defined constants are valid.
See for more info:
Why can't I initialize a variable-sized array?
Edit: Since you mentioned you are new to C++, here is a piece of code that might help you:
#include <iostream>
#include <vector>
#include <bitset>
int main()
{
unsigned int lines = 10;
const unsigned int columns = 5;
std::vector<std::bitset<columns>> matrixPublicity;
matrixPublicity.resize(lines);
for(int i=0; i < lines; i++)
{
for(int j=0; j < columns; j++)
std::cout << matrixPublicity[i][j] <<' ';
std::cout<<'\n';
}
}
note that in this case, columns must be constant.
Edit 2: And if the size of lines are not the same, then you must stick to vector types:
typedef std::vector<bool> matrixLine;
std::vector<matrixLine> matrixPublicity;
now you can use resize method for the i-th line of the matrix, e.g.
matrixPublicity[1].resize(number_of_columns_in_line_2);
What you are trying to do would be the same as this:
std::vector<unsigned int> v1 { 1, 2, 3, 4, 5 };
std::vector<unsigned int> v2 { 6, 7, 8, 9 };
bool mat[v1.size()][v2.size()] = false;
This is how the compiler will interpret it without the temporaries and this is invalid. When you declare an array of any type its size has to be known at compile time.
bool mat[2][3] = false; // still invalid
bool mat[2][3] = { false }; // Okay
const int x = 5;
const int y = 7;
bool mat[x][y] = false; // invalid
bool mat[x][y] = { false }; // okay
// Even this is invalid
std::vector<int> v1{ 1, 2, 3 };
std::vector<int> v2{ 4, 5, 6, 7 };
const std::size_t x1 = v1.size();
const std::size_t y1 = v2.size();
bool mat2[x1][y1] = { false }; // Still won't compile.
Value to declare an array must be a constant expression.
Instead of making an array as you have tried to do, you could make a class template that will construct a matrix like object for you. Here is what I have come up with, now the overall design or pattern of this template will fit your condition but the actual implementation to generate the internal matrix will depend on your data and what you intend.
#include <vector>
#include <iostream>
#include <conio.h>
template <class T, class U>
class Matrix {
private:
std::vector<T> m_lines;
std::vector<T> m_cols;
std::vector<U> m_mat;
std::size_t m_size;
std::size_t m_lineCount;
std::size_t m_colsCount;
public:
Matrix() {};
Matrix( const std::vector<T>& lines, const std::vector<T>& cols ) :
m_lines(lines),
m_cols(cols),
m_lineCount( lines.size() ),
m_colsCount( cols.size() )
{
addVectors( lines, cols );
}
void addVectors( const std::vector<T>& v1, const std::vector<T>& v2 ) {
m_lines = v1;
m_cols = v2;
m_lineCount = m_lines.size();
m_colsCount = m_cols.size();
for ( unsigned int i = 0; i < m_lineCount; ++i ) {
for ( unsigned int j = 0; j < m_colsCount); j++ ) {
// This will depend on your implementation and how you
// construct this matrix based off of your existing containers
m_mat.push_back(m_lines[i] & m_cols[j]);
}
}
m_size = m_mat.size();
}
std::size_t size() const { return m_size; }
std::size_t sizeRows() const { return m_lineCount; }
std::size_t sizelColumns() const { return m_colsCount; }
std::vector<U>& getMatrix() const { return m_mat; }
std::vector<T>& getLines() const { return m_lines; }
std::vector<T>& getColumns() const { return m_columns; }
bool operator[]( std::size_t idx ) { return m_mat[idx]; }
const bool& operator[]( std::size_t idx ) const { return m_mat[idx]; }
};
int main() {
std::vector<unsigned> v1{ 1, 0, 1, 1, 0 };
std::vector<unsigned> v2{ 0, 1, 1, 1, 0 };
Matrix<unsigned, bool> mat1( v1, v2 );
int line = 0;
for ( unsigned u = 0; u < mat1.size(); ++u ) {
line++;
std::cout << mat1[u] << " ";
if ( line == mat1.sizeRows() ) {
std::cout << "\n";
line = 0;
}
}
std::cout << "\nPress any key to quit.\n" << std::endl;
_getch();
return 0;
}
Output
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0
With this template class you can create a matrix of any type U by passing in two vectors for type T. Now how you construct the matrix will be implementation dependent. But this class is reusable for different types.
You could have two vectors of type doubles, and construct a matrix of unsigned chars, or you could have two vectors of user defined class or struct types and generate a matrix of unsigned values. This may help you out in many situations.
Note: - This does generate a compiler warning, no errors though and it prints and displays properly, but the compiler warning generated by MSVS 2015 is warning C4800: unsigned int: forcing value to bool true or false (performance warning)
This is generated for I am doing a bit wise & operation on to unsigned values; but that is why I set my initial vectors to be passed to this class template's constructor to have all 1s & 0s as this is meant for demonstration only.
EDIT - I made an edit to the class because I noticed I had a default constructor and had no way to add vectors to it, so I added an extra member variable, and an addVectors function, and moved the implementation from the defined constructor to the new function and just ended up calling that function in the defined constructor.
Creating an array isn't that difficult :)
A matrix (2D/3D/...-array) is unfortunately a little bit different if you want to do it your way!
But first of all you should know about the stack and the heap!
Lets have a look at these 2:
Stack:
A stack variable/array/matrix/... is only valid between the nearest 2 -> {} <- which you normally call a "codeblock". The size of it was defined during the "compile time" (the time where the compiler translates your code into the machine language). That means the size of your array needs to be set.
Example:
#include <iostream>
#define MACRO 128
int arraySize(int size){
std::cin >> size;
return size;
}
int main() {
//this is valid
int intArray[128] = {}; //the size(here: 128) needs to be a number like
//or a macro like 'MACRO' which is
//compile-time-only as well
//this is valid
int intArray2[MACRO] = {};
//this is not valid!
int intArray[size()] = {};
return 0;
}
Heap:
A heap variable/array/matrix/... is valid until you delete it. That also means that a heap var is created during the run-time(from starting your program until you close/stop it)! This is allows you to define it's size.
Example:
#include <iostream>
#define MACRO 128
int arraySize(int size){
return size;
}
int main() {
//this is valid
int intArray[128] = {}; //the size(here: 128) needs to be a number like
//or a macro like 'MACRO' whic is
//compile-time-only as well
//this is valid
int intArray2[MACRO] = {};
//creating an array with a non-static size
//works like this:
//int can also be a 'bool'
int* intArray = new int[arraySize()];
// ^ the star means you are pointing to
//an adress inside of your memory which has
//the size of an int (per element)
//That's why they are called "pointers"!
//Right now it points to the beginning of the
//array.
// ^ the keyword "new" says that
//you are allocating memory on the heap.
// ^
//then you have to say which kind of array
//it is which is the same you gave the pointer
// ^
//now you give it the size of that array
//this time it can be return value or the size
//of a variable
//as I mentioned...you have to delete this array on your own
//if you dont do that your program will crash
//maybe not after starting but it will!
//SO NEVER NEVER NEVER... forget about it
delete intArray[];
//^ write delete
// ^
//then the name of your array
// ^
//at the end of it write these 2 brackets
//thex say you wanna remove the whole array!
//why? because you can also create/delete
//heap variables not only arrays.
return 0;
}
Creating a matrix on the heap is unfortunately not that easy.
But it is essential to know how a 1D-array works before going to further dimensions! That's why I did this tutorial!
Klick here to see how to create a matrix on the heap
Klick here to learn more about the heap
Klick here to choose the best result of this theme
I hope I could help you :)!
#define ARRAY_SIZE 20
float DataSource[ARRAY_SIZE];
void Read(unsigned char const *Source, unsigned char *Destination, unsigned long DataSize)
{
for ( unsigned long i = 0; i < DataSize; i++)
{
*(Destination + i*DataSize) = *(Source + i*DataSize);
}
}
void fun()
{
int Index;
float Dest;
for ( Index = 0; Index < ARRAY_SIZE; Index++ )
{
Read((unsigned char *)&DataSource[Index], (unsigned char *)&Dest, sizeof(DataSource[Index]));
}
}
I'm having an issue with the above code where upon calling Read(), my Index variable gets overwritten and I am certain the ugly pointer casting is the culprit, but I'm having trouble understanding exactly what is happening here.
The unsigned char pointer types are mandatory because the above code is intended to simulate some driver level software and maintain the same prototype.
Can someone help me to understand the issue here? All the above code is changeable except for the prototype of Read().
The error is here:
for ( unsigned long i = 0; i < DataSize; i++)
{
// vvvvvvvvvv vvvvvvvvvv
*(Destination + i*DataSize) = *(Source + i*DataSize);
}
i * DataSize is always greater than i => "out of bound" access.
Replace with:
for ( unsigned long i = 0; i < DataSize; i++)
{
*(Destination + i) = *(Source + i);
}
You pass in a single float's address to Read (&Dest) and then proceed to write many valuese to consecutive memory locations. Since you're writing random memory at that point it's not unlikely that it could have overwritten index (and other stuff) because stacks usually grow downwards.
This is wrong:
*(Destination + i*DataSize) = *(Source + i*DataSize);
You want to copy DataSize adjacent bytes, not bytes DataSize apart (total span DataSize*DataSize)
Just say
Destination[i] = Source[i];
An amusing (to me) C++ way.
template<typename Data>
struct MemBlockRefHelper {
typedef Data value_type;
Data* data;
size_t size;
MemBlockRefHelper( Data* d, size_t s):data(d), size(s) {}
template<typename Target, typename Other=typename Target::value_type>
Target& Assign( MemBlockRefHelper<Other> const& other ) {
Assert(size == other.size);
for (size_t i = 0; i < size; ++i) {
if (i < other.size) {
data[i] = other.data[i];
} else {
data[i] = 0;
}
}
Target* self = static_cast<Target*>(this);
return *self;
}
};
struct MemBlockRef;
struct MemBlockCRef:MemBlockRefHelper<const unsigned char> {
MemBlockCRef( const unsigned char* d, size_t s ):MemBlockRefHelper<const unsigned char>( d, s ) {}
MemBlockCRef( const MemBlockRef& other );
};
struct MemBlockRef:MemBlockRefHelper<unsigned char> {
MemBlockRef( unsigned char* d, size_t s ):MemBlockRefHelper<unsigned char>( d, s ) {}
MemBlockRef& operator=( MemBlockRef const& other ) {
return Assign< MemBlockRef >( other );
}
MemBlockRef& operator=( MemBlockCRef const& other ) {
return Assign< MemBlockRef, const unsigned char >( other );
}
};
inline MemBlockCRef::MemBlockCRef( const MemBlockRef& other ): MemBlockRefHelper<const unsigned char>( other.data, other.size ) {}
void Read( unsigned char const* Source, unsigned char* Dest, unsigned long DataSize ) {
MemBlockCRef src( Source, DataSize );
MemBlockRef dest( Dest, DataSize );
dest = src;
}
massively over engineered, but the idea is to wrap up the idea of a block of POD memory of a certain size, and provide reference semantics to its contents (initialization is creating a new reference to the same data, assignment does a copy over the referred to data).
Once you have such classes, the code for Read becomes a 3 liner. Well, you can do it in one:
MemBlockRef( Dest, DataSize ) = MemBlockCRef( Source, DataSize );
but that is needless.
Well, so it this entire framework.
But I was amused by writing it.
Let's take a closer look at your Read(): i changes from 0 to DataSize-1; each time you access memory by an offset of i*DataSize... that is, by an offset from 0 to DataSize*(DataSize-1). Looks wrong, as DataSize**2-DataSize makes no sense.
Unlike other answers, I don't want to guess what you wanted. Just showing a kind of "dimensional analysis" that can help spotting the wrongest part of code without reading the author's mind.
You are treating the scalar variable Dest declared inside fun() as an array inside Read(). It seems that both Dest and your Index variable are placed adjacent on the stack which explains that Index gets overwritten exactly when the loop inside Read() is executed for i==1.
So the solution is: declare Dest as an array, too:
float Dest[ARRAY_SIZE];
I have a matrix declared like int **matrix, and I know that the proper way to pass it to a function to allocate memory should be like this:
void AllocMat(int ***mat, int size);
But now I need to delete these memory in another function and am not sure about what to pass:
void DeallocMat(int **mat, int size);
or
void DeallocMat(int ***mat, int size);
I think the second one should be right, but neither way gives me segmentation fault as I tried.
The question is tagged C++, and yet the answers only use the C subset...
Well, first of all, I would recommend against the whole thing. Create a class that encapsulates your matrix and allocate it in a single block, offer operator()(int,int) to gain access to the elements...
But back to the problem. In C++ you should use references rather than pointers to allow the function to change the argument, so your original allocate signature should be:
void AllocMat(int **&mat, int size);
And call it like:
int **matrix = 0;
AllocMat( matrix, 5 );
Or better, just return the pointer:
int **AllocMat( int size );
int **matrix = AllocMat( 5 );
For the deallocation function, since you don't need to modify the outer pointer, you can just use:
void DeallocMat( int**mat, int size ); // size might be required to release the
// internal pointers
Now, for a sketch of the C++ solution:
template <typename T> // no need to limit this to int
class square_matrix {
const unsigned size;
T * data;
public:
square_matrix( unsigned size ) : size(size), data( new T[size*size]() ) {}
square_matrix( matrix const & m ) : size( m.size ), data( new T[m.size*m.size] ) {
std::copy( m.data, m.data+size*size, data );
}
~matrix() {
delete [] data;
}
T const & operator()( unsigned x, unsigned y ) const {
// optional range check and throw exception
return data[ x + y*size ];
}
void set( unsigned x, unsigned y, T const & value ) {
// optional range check and throw exception
data[ x + y*size ] = value;
}
};
First is correct. But your real problem is that you are using pointers when there are better alternatives. For a 2d matrix you should use a vector of vectors
#include <vector>
typedef std::vector<std::vector<int> > Matrix;
Matix m;
Now there is no need to delete anything, so one less thing to go wrong.
void DeallocMat(int **mat, int size) - allows you to deallocate memory (since you have passed the value of mat only allowing to deallocate memory but not change mat)
void DeallocMat(int ***mat, int size) - allows you to deallocate memory and change the value of mat to NULL (since you have now passed a pointer to mat allowing you to change its value)
The extra "*" just handles the pointer to be behaved as call by reference. If you want to get the output from your function, you need an extra "*" in your declaration. In this case, you should pass the reference of your pointer (using &) to these functions.
The reason why you required to pass a pointer to double pointer because your local variable must required to reflect with the new updated memory
void Foo(int * a)
{
a = new int[10];
}
int main()
{
int *a = 0;
Foo( a );
}
Now the memory will be allocated but the pointer A will not be update because the value of pointer A is simply copied to another pointer variable which is parameter of Foo. Once the Foo is returned, a will remain 0. To make it refect that, you should write code like follows
void Foo(int ** a)
{
*a = new int[10];
}
int main()
{
int *a = 0;
Foo( &a );
}
Here you're passing the address of a pointer. The which means that, the value which contains in the pointer will be updated from the Foo function.You can debug through and see how it works.
If you're sure that you will not access the pointer anymore, please use the first type. Otherwise use the second one. Make sure that you set the pointer to NULL to avoid further memory corruptions or dangling pointers.
The thing that confuses me about your question is that most people would not declare a matrix as an int **. The reason for this is that you would be forced to then allocate it in a loop. Your allocation function would require two parameters, which are the dimensions of the array like this:
void AllocMat(int *** mat, int n, int m) {
int ** result = new int * [ n ];
for (int x=0; x<n; x++) {
result[x] = new int [ m ];
}
*mat = result;
}
If this were the case, the corresponding deallocation function would require knowledge of the size of n as follows:
void DeallocMat(int *** mat, int n) {
if (mat == NULL || *mat == NULL) return;
int ** tmp = *mat;
for (int x=0; x<n; x++) {
if (tmp[x] != NULL) delete [] tmp[x];
}
delete [] tmp;
*mat = NULL;
}
With this approach, you could access your matrix like this:
int ** mat = NULL;
AllocMat(&mat, n, m);
for (int x=0; x<n; x++) {
for (int y=0; y<m; y++) {
mat[x][y] = 1;
}
}
DeallocMat(&mat, n);
Usually, people allocate matrices as a single buffer of memory to avoid extra allocations and pointer indirections, which is how I recommend you do it. In that case, you allocation function would look like this:
void AllocMat2(int ** mat, int n, int m) {
*mat = new int [ n * m ];
}
And the corresponding deallocation function like this:
void DeallocMat2(int ** mat) {
if (mat != NULL && *mat != NULL) {
delete [] *mat;
*mat = NULL;
}
}
And you would access it follows:
int * mat2 = NULL;
AllocMat2(&mat2, n, m);
for (int x=0; x<n; x++) {
for (int y=0; y<m; y++) {
mat2[x * n + y] = 1;
}
}
DeallocMat2(&mat2);
Either way works, but if you pass a pointer to the pointer you need to dereference it first. And the size parameter is redundant.
void DeallocMat(int **mat)
{
delete[] mat;
}
void DeallocMat(int ***mat)
{
delete[] *mat;
*mat = NULL;
}
I'm working on a little project for college, and I need to model transmission over network, and to impment and visualize different sorts of error correction algorithms. My improvized packet consists of one quint8: I need to convert it into a bit array, like QBitArray, append a check bit to it, trasfer it over UDP, check the success of transmission with the check bit, and then construct quint8 out of it.
Once again, it's not a practical but educational task, so don't suggest me to use real algoriths like CRC...
So my question is: how do I convert any data type (in this case quint8) into QBitArray? I mean any data in computer is a bit array, but how do I access it is the question.
Thanks, Dmitri.
Lets see if we can get it correct
template < class T >
static QBitArray toQBit ( const T &obj ) {
int const bitsInByte= 8;
int const bytsInObject= sizeof(T);
const quint8 *data = static_cast<const quint8*>(&obj) ;
QBitArray result(bytsInObject*bitsInByte);
for ( int byte=0; byte<bytsInObject ; ++byte ) {
for ( int bit=0; bit<bitsInByte; ++bit ) {
result.setBit ( byte*bitsInByte + bit, data[byte] & (1<<bit) ) ;
}
}
return result;
}
void Foo () {
Bar b ;
QBitArray qb = toQBit ( b ) ;
}
qint8 is actually signed char. So you can treat your objs as a char array.
template < class T >
QBitArray toQBit ( T &obj ) {
int len = sizeof(obj) * 8 ;
qint8 *data = (qint8*)(&obj) ;
QBitArray result ;
for ( int i=0; i< sizeof(data); ++i ) {
for ( int j=0; j<8; ++j ) {
result.setBit ( i*8 + j, data[i] & (1<<j) ) ;
}
}
return result;
}
void Foo () {
Bar b ;
QBitArray qb = toQBit ( b ) ;
}
I don't see any point in declaring template function and then casting its argument to uint8.
Solution for types that can be promoted to unsigned long
#include <bitset>
template <typename T>
QBitArray toQBit(T val) {
std::bitset<sizeof(T) * 8> bs(val);
QBitArray result(bs.size());
for (int ii = 0; ii < bs.size(); ++ii) {
result.setBit(ii, bs.test(ii));
}
return result;
}
There is no way to generically convert any data type to bit array. Especially if your data type contains pointers you probably want to transfer the pointee not the pointer. So any complex type should be treated separately. And be aware about different endiannes (little-endian and big-endian) in different architectures. I think that std::bitset is safe according to this problem, but for example casting a pointer to struct to a char array and storing its bits may not be safe.