struct thread_data
{
int seq, packetNum;
bool ackTally[4];
};
main:
bool boolArray[4];
data[j].packetNum = 3;
data[j].seq = 1;
data[j].ackTally = boolArray;
Running this code gives me this error
error: incompatible types in assignment of bool [1] to bool [0]
data[j].ackTally = boolArray;
How do I resolve this in code? (It may not be relevant by I'll probably want to change some of these values later within the main code)
C-style arrays cannot be assigned. There are two simple ways around this:
First, use std::copy instead of assignment:
#include <algorithm>
#include <iterator>
struct thread_data
{
int seq, packetNum;
bool ackTally[4];
};
int main() {
thread_data data;
bool boolArray[4];
// Initialize boolArray.
std::copy( std::begin(boolArray), std::end(boolArray),
std::begin(data.ackTally) );
}
Or second, replace your C-style arrays with std::array. One of several benefits of std::array is that it can be assigned:
#include <array>
struct thread_data
{
int seq, packetNum;
std::array<bool, 4> ackTally;
};
int main() {
thread_data data;
std::array<bool, 4> boolArray;
// Initialize boolArray.
thread_data.ackTally = boolArray;
}
Arrays can not be assigned. Each element must be copied in a loop. You don't have to write that loop yourself though, since there is an algorithm for that in the standard library: std::copy.
P.S. Copying from boolArray will have undefined behaviour because its values are indeterminate.
Related
Say I get an int from a lambda function ran at initialization of a class object. Is it possible to use that int to define the size of a std::array? Something like the following code.
#include <array>
#include <vector>
#include <iostream>
class Test1 {
public:
Test1( std::vector<int> vec1 ) :
nvars([&vec1]() -> int { return vec1.size()+1; }())
{
};
const int nvars;
// ******This isn't allowed!!!!!!!!!
const std::array<int,nvars> arr;
};
int main() {
std::vector<int> vec{1,2,3,4};
Test1 test1(vec);
std::cout << "nvars: " << test1.nvars << std::endl;
return 0;
}
I am a C++ beginner so any other advice will be welcome.
No. The size of the array is part of its type. You cannot let it be determined at runtime.
You can have it be determined at compile time, if you do pass a std::array to the constructor. Since C++17 there is CTAD (class template argument deduction) which lets you write:
#include <array>
template <size_t N>
class Test1 {
public:
Test1( std::array<int,N> vec1 ) :
arr(vec1)
{
};
const std::array<int,N> arr;
};
int main() {
std::array vec{1,2,3,4};
Test1 test1(vec);
}
Live Demo
test1 is of type Test1<4>. Note that Test1<4> is a distinct different type than eg Test<5> or Test<24>. If you want one type to have a member array of different size, make it a std::vector.
I'm trying to write something similar to this:
void loadImage(SDL_Renderer *ren, {SDL_Texture *&texture, const char *imagePath} ... ){
for(int i = 0; i < numOfPairs; i++){
SDL_Surface *curImage = IMG_Load(pairs[i].second);
pairs[i].first = SDL_CreateTextureFromSurface(ren, curImage);
SDL_FreeSurface(curImage);
}
}
Where I have a variable number of pairs and each pair contains a texture and its correspondent path. I've no idea, which way would be best to approach this problem. I've thought about using the <cstdarg> library's variadic functions but I read on another question that that's not a good solution.
the way that immediately comes to mind is std::vector. You could pass a vector of std::pair or of std::tuple if you are using a c++11 or better compiler.
very simple to do
#include <vector>
#include <tuple>
....
std::vector<std::tuple<SDL_texture, const char *>> args;
args.push_back(std::make_tuple(texture1, path1));
args.push_back(std::make_tuple(texture2, path2));
then your func
void load_Image(SDL_renedred *ren, const std::vector<std::tuple<SDL_texture, const char *>> &args)
{
std::tuple<SDL_textture, const char*> &arg1 = args[0];
// or if you have modern c++
auto &arg1 = args[0];
}
(not compiled, probably got typos in it.)
Although I hate it, you can use std::initializer_list like:
#include <initializer_list>
#include <tuple>
void loadImage(SDL_Renderer *ren, std::initializer_list<std::tuple<SDL_Texture*&,const char *&>> pair_list ){
for(auto p:pairlist){//initializer_list cannot be indexed but only iterated
SDL_Surface *curImage = IMG_Load(p.second);
p.first = SDL_CreateTextureFromSurface(ren, curImage);
SDL_FreeSurface(curImage);
};
};
//call site:
loadImage(ren,{tie(tex1,path1),tie(tex2,path2),tie(tex3,path3)});
It is recommended to store your pointers in std::unique_ptr instants with costume deleter. And I guess you'd better change your data structue design to avoid tying objects like that - e.g. using a std::map:
#include <initializer_list>
#include <memory>
#include <map>
struct MySDLDeleter{
void operator(SDL_Texture* texPtr){SDL_FreeTexture(texPtr);};
void operator(SDL_Renderer * renPtr){SDL_FreeRenderer(renPtr);};
void operator(SDL_Surface * srfPtr){SDL_FreeSurface(srfPtr);};
};
auto loadImage(std::unique_ptr<SDL_Renderer,MySDLDeleter> ren, std::initializer_list<const char *> path_list ){
std::map<const char *,std::unique_ptr<SDL_Texture,MySDLDeleter> texMap;
for(auto p:path_list ){
std::unique_ptr<SDL_Surface ,MySDLDeleter> curImage = IMG_Load(p.second);
texMap.insert(p,SDL_CreateTextureFromSurface(ren.get(), curImage.get()));
};
return texMap;
};
//call site:
auto texMap=loadImage(create_the_renderer_ptr(),{path1,path2,path3});
First of all write a function that will process each pair (i.e. what you have inside your for loop):
using ImagePair = std::pair<SDL_Texture*&, const char*>;
void processImage(ImagePair imgPair)
{
SDL_Surface *curImage = IMG_Load(imgPair.second);
imgPair.first = SDL_CreateTextureFromSurface(ren, curImage);
SDL_FreeSurface(curImage);
}
Then if you have C++11 or above, you can use a brace-initialization trick to call processImage() for each argument:
template <typename ... Ts>
void loadImage(Ts ... ts)
{
using dummy = int[];
(void)dummy {0, (processImage(ts)), 0)... };
}
What you're doing here is taking advantage of the fact that the compiler knows it has to do a pack expansion inside a brace-initialization list. That way you can avoid having to write recursive variadic template functions yourself.
Here you are building a dummy array of integers, which is equal in size to the number of variadic arguments you pass in. For each argument you call the function processImage(), but using the comma operator you set the value in the array to 0. So you expand the pack and call processImage() for each argument while creating a dummy array. This answer might explain it more clearly.
If you have C++17 or above, you can simplify further and use a fold expression:
template<typename... Ts>
void loadImage(Ts... args)
{
(processImage(args),...);
}
I need to copy the contents of a std::list into an array, wherein the array is struct of array. Below is the code implementation of it.
#include <iostream>
#include <string>
using namespace std;
typedef struct
{
int height;
int width;
int length;
}dimensions;
GetDimensions(list<std::string>, *int); // Function that copies the content of list to array passed as second parameter
int main()
{
dimensions cuboid[10];
int plane[10];
list<std::string> planeList = GetList();//Function that returns list of elements
list<std::string> dimensionList = GetList();
GetDimensions(planeList,&plane);//This is fine, as it is a simple array
GetDimensions(dimensionList,&cuboid.height);//Trouble in implementation of this usecase, for cuboid.height, cuboid.width and cuboid.height.
return 0;
}
GetDimensions(list<std::string>dimensionList, int* dimensionParams)
{
int i=0;
for(list<std::string>::iterator it = dimensionList.begin(); it != dimensionList.end(); ++it)
{
dimensionParams[i] = stoi(*it);
i++;
}
}
Here, I need GetDimensions() function to copy the list (passed as first parameter) to array (second parameter). The implemented function works well for simple array plane. But how to pass the array of struct as parameter to the function ?
I will be getting the std::list as cuboid.height, cuboid.width and cuboid.length. So the function has to copy the contents of list from cuboid[0].height to cuboid[i].height respectively. Is there any specific function to copy the content directly?
Use std::array 's instead. Then your problem can be reduced to passing two different types of arrays to a single function.
This can be solved
either by good old function overloads
or in c++17 function template with
if-constexpr.
Following is an example code with templated function with if-constexpr (See live online)
#include <iostream>
#include <string>
#include <list>
#include <array>
#include <type_traits> // std::is_same_v
struct dimensions // no need to typedef here
{
int height;
int width;
int length;
};
template<typename T>
void GetDimensions(const list<std::string>& dimensionList, T& dimensionParams)
^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //---> pass list by const-ref as the values are non-modifying
{
int i{0};
if constexpr (std::is_same_v<std::array<int, 10>, T>)
{
for(const std::string& str: dimensionList) dimensionParams[i++] = std::stoi(str);
}
else
{
for(const std::string& str: dimensionList) dimensionParams[i++].height = std::stoi(str);
}
}
int main()
{
std::array<dimensions, 10> cuboid; // use std::array instead of VLA
std::array<int, 10> plane;
std::list<std::string> planeList{"1", "2"}; // some list
std::list<std::string> dimensionList{"1", "2"};
GetDimensions(planeList, plane);
GetDimensions(dimensionList, cuboid);
return 0;
}
Also note that:
You have not specified the return type of GetDimensions function.
You probably want to return void there.
in C++ you do not need to use typedef alias for struct { ... }.
last but not least, do not practice with using namespace std;
You can do this with boost::transform_iterator.
#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
#include <boost/iterator/transform_iterator.hpp>
struct dimensions {
int height;
int width;
int length;
};
template <typename OutputIt>
void GetDimensions(std::list<std::string> dimensionList, OutputIt dimensionParams)
{
// N.b. taking the address of a standard library function is undefined, so wrap in a lambda
auto stoi = [](std::string s){ return std::stoi(s); };
std::copy(boost::make_transform_iterator(dimensionList.begin(), stoi),
boost::make_transform_iterator(dimensionList.end(), stoi),
dimensionParams);
}
int main() {
dimensions cuboid[10];
int plane[10];
std::list<std::string> planeList = GetList();
std::list<std::string> heightList = GetList();
std::list<std::string> widthList = GetList();
std::list<std::string> lengthList = GetList();
GetDimensions(planeList, plane);
GetDimensions(heightList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::height)));
GetDimensions(widthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::width)));
GetDimensions(lengthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::length)));
return 0;
}
I've been trying to use arrays in a class constructor. Here is my code:
struct Motor_Group{
int Motors[3];
int Encoder;
};
int main()
{
Motor_Group Left_Drive {{2,3},3};
Motor_Group Right_Drive {{2,3},3};
cout<< sizeof(Left_Drive.Motors)/sizeof(int);
return 0;
}
But, the problem is that i want to make the length of the array motors to be undefined untill its contents is declared. How can i do that?
Thanks for your kind help!
If you do not need MotorGroups to be of the same type,
then you could template the array size.
Using std::array
#include <array>
#include <iostream>
template<size_t motor_count>
struct Motor_Group{
std::array<int,motor_count> Motors;
int Encoder;
};
int main()
{
Motor_Group<2> Left_Drive {{2,3},3};
Motor_Group<3> Right_Drive {{2,3,4},3};
std::cout<< Left_Drive.size();
// Left_Drive = Right_Drive; // Error at compile time, since Motor_Group<2> != Motor_Group<3>
return 0;
}
I have an class which I wish to instantiate by passing an array of values. The object here has two members but this has been reduced for illustration. In the future I will read values from disk and then create an object from those values, hence the array. The object will have multiple pointers later on hence the shared_ptr.
Firstly, I would like to know if this would prevent memory leaks as is. Secondly, I would like to know if there is are less bloated ways of instantiating objects and then determinsitically destroying them later on.
Class Header file:
//MyClass.hpp
#pragma once
#include "stdafx.h"
#include <array>
class SimpleClass{
private:
//Per object
double var1;
double var2;
public static:
//For calling when using templates such as std::array<>
//Using __int8 as will not be > 256
static const uint8_t varCount 2;
SimpleBody(std::array<double, varCount> inputArray);
~SimpleBody();
}
Class Implementation
//MyClass.cpp
#pragma once
#include "stdafx.h"
#include <array>
#include "body.hpp"
SimpleBody::SimpleBody(std::array<double, SimpleBody::varCount> inputArray ) {
//Assign var1
MyClass::var1= inputArray[0];
//Assign var2
MyClass::var2= inputArray[1];
};
SimpleBody::~SimpleBody() {
//Add in code here when children need deleting
};
Entry Point
// EntryPoint.cpp
//
#include "stdafx.h"
#include "MyClass.hpp"
#include <array>
#include <memory>
int main()
{
//Create an array with a smart pointer for memory management
std::unique_ptr< std::array<double, MyClass::varCount> > initArray =
std::make_unique< std::array<double, MyClass::varCount> >();
//Define values
*initArray = { 1.0,2.0 };
//Use array to create object
std::shared_ptr<MyClass> object = std::make_shared<MyClass>(*initArray );
//Free memory
initArray.reset();
object.reset();
return 0;
}
In your example you could skip the unique pointer because you only want the values anyway
int main()
{
std::array<double, MyClass::varCount> initArray = { 1.0,2.0 };
std::shared_ptr<MyClass> object = std::make_shared<MyClass>(initArray );
return 0;
}
This would do the same.
For "less bloated":
There is always auto:
int main()
{
auto initArray = std::make_unique< std::array<double, MyClass::varCount> >();
*initArray = {1.0,2.0};
auto object = std::make_shared<MyClass>(*initArray );
return 0;
}
First of all its bad if you pass the array by value in the SimpleBody constructor. Better use a reference
SimpleBody(const std::array<int, varCount> &inputArray);
At the moment, you construct your shared_ptr the unique_ptr looses the ownership of the array. You don't need to do reset manually. Also, at the moment, your program hits the } line, the memory will be freed.