In the first stage, i've created an object Planet with some attributes, like name, type and distanceToEarth. I've created a Repository then, basically a structure consisting of a dynamic array elems and its length and maximum capacity.
typedef enum {
NEPTUNE_LIKE,
GAS_GIANT,
TERRESTRIAL,
SUPER_EARTH,
UNKNOWN
}PlanetType;
typedef struct {
char name[30];
PlanetType type;
float distanceToEarth;
}Planet;
Planet createPlanet(char name[], PlanetType type, double distance) {
Planet pl;
strcpy(pl.name, name);
pl.distanceToEarth = distance;
pl.type = type;
return pl;
}
typedef struct
{
Planet* elems; /** dynamic array containing the planets */
int length; /** actual length of the array */
int capacity; /** maximum capacity of the array */
} PlanetRepo;
PlanetRepo createPlanetRepo(int capacity) {
/// create a new planet repo; the elems field must be dynamically allocated (malloc)
PlanetRepo r;
r.capacity = capacity;
r.length = 0;
r.elems = (Planet*) malloc(sizeof(Planet)*capacity);
return r;
}
bool remove(PlanetRepo* repo, Planet pl) {
/// #todo remove planet pl from the repository
/// return false if the planet does not exist in the repository
return false;
}
My problem is related to the function remove(). I can't figure out how I am supposed to remove that object from a dynamically allocated array.
Of course, this is not the entire code, but I've selected only the relevant parts. If I forgot to include something, let me know.
Since you insisted on tagging C++, rather than C:
In C++ you wouldn't define the PlanetRepo and the associated functions at all. Instead you would simply declare a variable of type
std::vector<Planet>
or maybe depending on the use case (but less likely)
std::list<Planet>
Both of these already have member functions .erase that remove elements from them.
In C++ you also wouldn't write
typedef struct {
char name[30];
PlanetType type;
float distanceToEarth;
}Planet;
but instead
struct Planet {
char name[30];
PlanetType type;
float distanceToEarth;
};
and you would most likely use std::string instead of char[30] as type for name.
Instead of a function Planet createPlanet(char name[], PlanetType type, double distance) you would define a constructor for Planet:
struct Planet {
std::string name;
PlanetType type;
float distanceToEarth;
Planet(std::string name, PlanetType type, double distance)
: name(name), type(type), distance(distance)
{}
};
and probably make the members private.
You also wouldn't define an unscoped enum, but a scoped one instead (since C++11), see Why is enum class preferred over plain enum?.
Since this is rather a C than a C++ program, you could use a linked list which makes it possible for you to delete elements in a dynamically allocated "array".
This might be of interest.
Like mentioned before C++ has implemented data structures so you can easily store your planets. But you can do something like:
bool remove(PlanetRepo* repo, Planet pl) {
PlanetRepo* temp = (PlanetRepo*) malloc(sizeof(Planet)*repo->capacity);
if(!temp){
return false;
}
temp->capacity = repo->capacity;
temp->size = repo->size-1;
for(int i = 0; i < repo->size; ++i){
if(repo[i] != pl){ //This won't work. How to compare Planets is left as an exercise to the reader
temp[i] = repo[i];
}
}
free(repo);
repo = temp;
return true;
}
Related
I have recently picked up C++ but I am having problems in some areas as I am used to Java and I am not sure if I am going about things the right way.
I have a text file which lines are structured like this
C;101;1;1;4;10;
H;102;9;0;1;8;2
C;103;9;9;2;5;
C;104;0;9;3;8;
; being the delimeter
C is used to check if its a Crawler(H is used to check if it is a Hopper), it is then followed by the id, positions, directions(1-4), size, and alive(0/1)
I have an abstract Bug class then I have a Crawler and Hopper class which both inherit from the Bug class.
Here is my Bug class
class Bug
{
public:
int id;
pair<int, int> position;
int direction;
int size;
bool alive;
list<pair<int, int>> path;
virtual void move() {}
bool isWayBlocked() {}
Bug(string type, int id, pair <int, int> position, int direction, int size, bool alive)
{
type = type;
id = id;
position = position;
direction = direction;
size = size;
alive = alive;
};
};
Here is the Crawler class
class Crawler : public Bug
{
public:
Crawler(int id, pair <int, int> position, int direction, int size)
: Bug("Crawler", id, position, direction, size, alive)
{
}
};
Here is an example of me trying to create an instance of a Crawler object:
Bug* bugPtr;
if (bugType == "C") {
bugPtr = new Crawler(bugID, { xPos,yPos }, direction, size);
//Crawler* CrawlerBugPointer = new Crawler(bugID, { xPos,yPos }, direction, size);
}
I am able to easily read from the text file and store the variables but when i start debugging all of the fields in the bugPtr say 0, I suspect that I have gone about things in completely the wrong way and have not built the Abstract class and Inheritor classes properly, could anyone point out what I am doing wrong? any help will be much appreciated.
The compiler should have generated a warning, "Expression with possibly no effect" (or similar), for every line in your constructor. Read the warnings - they're useful.
Bug(string type, int id, pair <int, int> position, int direction, int size, bool alive)
{
this->type = type;
this->id = id;
this->position = position;
this->direction = direction;
this->size = size;
this->alive = alive;
};
While C++ doesn't require the ridiculous use of this everywhere, in your case it's necessary since you've given the constructor parameters the same names as the fields. When you enter something like
id = id;
You're saying, "Find the variable with name id in this scope, and assign its value to itself." Which does nothing, which is why all of your fields were left at their default values. Saying this.id means, "Find the class field with name id and assign the value of the local id to it."
The way to do this is with an initializer list:
Bug(string type, int id, pair <int, int> position, int direction, int size, bool alive)
: type(type), id(id), position(position), direction(direction), size(size), alive(alive)
{ }
The problem with the original code is name hiding. In the constructor body, type = type simply assigns the value of the argument named type to the argument named type. It doesn’t change the value of the member named type.
But in an initializer list, in an entry like type(type), the first type is the name of the member and the second type is the name of the argument. So the member gets the argument value, and all is well.
You should always use initializer lists in constructors. In addition to allowing duplicate names (if that’s what you like; many programmers don’t) they are more efficient when you have data members with more complex initialization.
Guys I have a function like this (this is given and should not be modified).
void readData(int &ID, void*&data, bool &mybool) {
if(mybool)
{
std::string a = "bla";
std::string* ptrToString = &a;
data = ptrToString;
}
else
{
int b = 9;
int* ptrToint = &b;
data = ptrToint;
}
}
So I want to use this function in a loop and save the returned function parameters in a vector (for each iteration).
To do so, I wrote the following struct:
template<typename T>
struct dataStruct {
int id;
T** data; //I first has void** data, but would not be better to
// have the type? instead of converting myData back
// to void* ?
bool mybool;
};
my main.cpp then look like this:
int main()
{
void* myData = nullptr;
std::vector<dataStruct> vec; // this line also doesn't compile. it need the typename
bool bb = false;
for(int id = 1 ; id < 5; id++) {
if (id%2) { bb = true; }
readData(id, myData, bb); //after this line myData point to a string
vec.push_back(id, &myData<?>); //how can I set the template param to be the type myData point to?
}
}
Or is there a better way to do that without template? I used c++11 (I can't use c++14)
The function that you say cannot be modified, i.e. readData() is the one that should alert you!
It causes Undefined Behavior, since the pointers are set to local variables, which means that when the function terminates, then these pointers will be dangling pointers.
Let us leave aside the shenanigans of the readData function for now under the assumption that it was just for the sake of the example (and does not produce UB in your real use case).
You cannot directly store values with different (static) types in a std::vector. Notably, dataStruct<int> and dataStruct<std::string> are completely unrelated types, you cannot store them in the same vector as-is.
Your problem boils down to "I have data that is given to me in a type-unsafe manner and want to eventually get type-safe access to it". The solution to this is to create a data structure that your type-unsafe data is parsed into. For example, it seems that you inteded for your example data to have structure in the sense that there are pairs of int and std::string (note that your id%2 is not doing that because the else is missing and the bool is never set to false again, but I guess you wanted it to alternate).
So let's turn that bunch of void* into structured data:
std::pair<int, std::string> readPair(int pairIndex)
{
void* ptr;
std::pair<int, std::string> ret;
// Copying data here.
readData(2 * pairIndex + 1, ptr, false);
ret.first = *reinterpret_cast<int*>(ptr);
readData(2 * pairIndex + 2, ptr, true);
ret.second = *reinterpret_cast<std::string*>(ptr);
}
void main()
{
std::vector<std::pair<int, std::string>> parsedData;
parsedData.push_back(readPair(0));
parsedData.push_back(readPair(1));
}
Demo
(I removed the references from the readData() signature for brevity - you get the same effect by storing the temporary expressions in variables.)
Generally speaking: Whatever relation between id and the expected data type is should just be turned into the data structure - otherwise you can only reason about the type of your data entries when you know both the current ID and this relation, which is exactly something you should encapsulate in a data structure.
Your readData isn't a useful function. Any attempt at using what it produces gives undefined behavior.
Yes, it's possible to do roughly what you're asking for without a template. To do it meaningfully, you have a couple of choices. The "old school" way would be to store the data in a tagged union:
struct tagged_data {
enum { T_INT, T_STR } tag;
union {
int x;
char *y;
} data;
};
This lets you store either a string or an int, and you set the tag to tell you which one a particular tagged_data item contains. Then (crucially) when you store a string into it, you dynamically allocate the data it points at, so it will remain valid until you explicitly free the data.
Unfortunately, (at least if memory serves) C++11 doesn't support storing non-POD types in a union, so if you went this route, you'd have to use a char * as above, not an actual std::string.
One way to remove (most of) those limitations is to use an inheritance-based model:
class Data {
public:
virtual ~Data() { }
};
class StringData : public Data {
std::string content;
public:
StringData(std::string const &init) : content(init) {}
};
class IntData : public Data {
int content;
public:
IntData(std::string const &init) : content(init) {}
};
This is somewhat incomplete, but I think probably enough to give the general idea--you'd have an array (or vector) of pointers to the base class. To insert data, you'd create a StringData or IntData object (allocating it dynamically) and then store its address into the collection of Data *. When you need to get one back, you use dynamic_cast (among other things) to figure out which one it started as, and get back to that type safely. All somewhat ugly, but it does work.
Even with C++11, you can use a template-based solution. For example, Boost::variant, can do this job quite nicely. This will provide an overloaded constructor and value semantics, so you could do something like:
boost::variant<int, std::string> some_object("input string");
In other words, it's pretty what you'd get if you spent the time and effort necessary to finish the inheritance-based code outlined above--except that it's dramatically cleaner, since it gets rid of the requirement to store a pointer to the base class, use dynamic_cast to retrieve an object of the correct type, and so on. In short, it's the right solution to the problem (until/unless you can upgrade to a newer compiler, and use std::variant instead).
Apart from the problem in given code described in comments/replies.
I am trying to answer your question
vec.push_back(id, &myData<?>); //how can I set the template param to be the type myData point to?
Before that you need to modify vec definition as following
vector<dataStruct<void>> vec;
Now you can simple push element in vector
vec.push_back({id, &mydata, bb});
i have tried to modify your code so that it can work
#include<iostream>
#include<vector>
using namespace std;
template<typename T>
struct dataStruct
{
int id;
T** data;
bool mybool;
};
void readData(int &ID, void*& data, bool& mybool)
{
if (mybool)
{
data = new string("bla");
}
else
{
int b = 0;
data = &b;
}
}
int main ()
{
void* mydata = nullptr;
vector<dataStruct<void>> vec;
bool bb = false;
for (int id = 0; id < 5; id++)
{
if (id%2) bb = true;
readData(id, mydata, bb);
vec.push_back({id, &mydata, bb});
}
}
I am brand new to SO, and it all looks very helpful.
My code is being used for cryengine, but this seems to be a good all around c++ problem. And lets face it, the official CE forums blow.
The trouble I'm having is accessing the const char* variable of a struct array outside of the scope I'm assigning the variable in.
BuildingManager.h
class CBuildingManager {
public:
struct SBuilding {
const char* name;
};
struct SBuilding buildings[999];
//string buildingName;
const char* buildingList[999];
};
BuildingManager.cpp
void CBuildingManager::LoadBuildingXML() {
int n = -1;
const char *name;
const char *value;
//XML iterator is long and not necessary for example. n++ per iteration.
//it just works
//last part of iterator
for (size_t j = 0; j < tags->getNumAttributes(); j++) {
//Get building name in XML. This works
tags->getAttributeByIndex(j, &name, &value);
//assign building name to name in struct
buildings[n].name = value;
CryLog("%s", buildings[n].name);
buildingList[n] = buildings[n].name;
}
}
}
}
}
void CBuildingManager::LogAction(int x) {
//x modified by input. also works
CryLog("%c", buildingList[x]); //this, however, does not
}
So basically, I can print the building name as a string inside of the iterator, and it prints the whole building name (ie. "House")
But when I call LogAction, the building name will only print as a char, and will only show a single random symbol.
How can I convert the name in the struct to a string or otherwise get it to print as a whole word outside of the iterator?
Please let me know if my question is vague or shaky, and I will do my best to clean it up.
-Moose
I have really been struggling with a piece of code for a couple days. The error message i receive when i run my code is:
error: array initializer must be an initializer list
accountStore (int size = 0) : accts(size) { }
There seem to be others with similar problems here but unfortunately I am unable to apply their solutions (either don't work or not applicable).
What I am simply attempting to do is create a container class (array, can't use vectors) of a class 'prepaidAccount' but I am just unable to get the constructor portion of the container class 'storeAccount' to work. See code snippet below:
class prepaidAccount{
public:
//prepaidAccount ();
prepaidAccount(string newPhoneNum, float newAvailBal) : phoneNumber(newPhoneNum), availableBalance (newAvailBal){} //constructor
double addBalance(double howMuch) {
availableBalance = howMuch + availableBalance;
return availableBalance;
}
double payForCall(int callDuration, double tariff) {
callDuration = callDuration/60; //convert to minutes
double costOfCall = callDuration * tariff;
if (costOfCall > availableBalance) {
return -1;
}
else {
availableBalance = availableBalance - costOfCall;
return costOfCall;
}
}
void setAvailBal(int newAvailBal) {availableBalance = newAvailBal;}
float getAvailBal() {return availableBalance;}
void setPhoneNum(string newPhoneNum) {phoneNumber = newPhoneNum;}
string getPhoneNum() const {return phoneNumber;}
private:
string phoneNumber;
float availableBalance;
};
class accountStore { //made to store 100 prepaid accounts
public:
accountStore (int size = 0) : accts(size) { }
....
private:
prepaidAccount accts[100];
}
In main I simply call accountStore Account;
Any help is absolutely welcome. I very recently started learning c++ and about classes and constructors so please bear with me.
Thanks
You can't initialize an array with int like accountStore (int size = 0) : accts(size) {}.
prepaidAccount doesn't have a default constructor, you have to write member initialization list like,
accountStore (int size = 0) : accts{prepaidAccount(...), prepaidAccount(...), ...} { }
The array has 100 elements, it's not a practical solution here.
As a suggestion, think about std::vector, which has a constructor constructing with the spicified count of elements with specified value. Such as,
class accountStore {
public:
accountStore (int size = 0) : accts(size, prepaidAccount(...)) { }
....
private:
std::vector<prepaidAccount> accts;
};
Given that you have specified that you do not want to use a container such as std::vector but would like to specify the size at runtime, your only option would be to manually implement dynamic allocation yourself. Also given that you are wanting create 100 objects at a time, I would suggest making a function that can construct a temporary object according to your needs and then use this to initialise your dynamically allocated array. Consider the below code as a good starting point. (WARNING untested code.)
class prepaidAccount {
public:
// Constructor
prepaidAccount(string newPhoneNum, float newAvailBal)
: phoneNumber(newPhoneNum), availableBalance(newAvailBal) {}
// Default Constructor needed for dynamic allocation.
prepaidAccount() {}
/* your code*/
};
// Used to construct a tempoary prepaid account for copying to the array.
// Could use whatever constructor you see fit.
prepaidAccount MakePrepaidAccount(/*some parameters*/) {
/* Some code to generate account */
return some_var;
}
class accountStore {
public:
// Explicit constructor to avoid implicit type-casts.
explicit accountStore(const int &size = 0)
: accts(new prepaidAccount[size]) {
for (int i = 0; i < size; i++) {
// Will call defualt assignment function.
prepaidAccount[i] = MakePrepaidAccount(/*some parameters*/);
}
}
// Destructor
~accountStore() {
// Cleans up dynamically allocated memory.
delete[] prepaidAccount;
}
prepaidAccount *accts;
};
Edit: Amongst the c++ community it is often questionable when choosing to use dynamic allocation when there is such an excellent and comprehensive library of smart pointers. For example an std::vector would be perfect in this situation.
How it is possible to provide all three functions: msgpack_pack, msgpack_unpack and msgpack_object (also, what are meanings of them, exactly?) for a user-defined C++ class (in the same way MSGPACK_DEFINE does it for non-array POD/UD types) containing Plain Old Data arrays (such as dobule[] or char[]), so my class will play nicely with higher-level classes, containg this class in map or a vector?
Is there any examples of implementing them for your own class or at least msgpack C++ api documentation?
The only link to possible api reference i've found was http://redmine.msgpack.org/projects/msgpack/wiki ; but it is dead now.
Say, i have a struct like
struct entity {
const char name[256];
double mat[16];
};
What would be a msgpack_* member functions for it?
Thanks to guy who -1'd my question, i felt grievance and explored the actual undocumented codebase of msgpack. Here is the example of mentioned earlier functions with sort of explanation, in amount of my (vastly incomplete due to missing docs) understanding:
struct entity {
char name[256];
double mat[16];
// this function is appears to be a mere serializer
template <typename Packer>
void msgpack_pack(Packer& pk) const {
// make array of two elements, by the number of class fields
pk.pack_array(2);
// pack the first field, strightforward
pk.pack_raw(sizeof(name));
pk.pack_raw_body(name, sizeof(name));
// since it is array of doubles, we can't use direct conversion or copying
// memory because it would be a machine-dependent representation of floats
// instead, we converting this POD array to some msgpack array, like this:
pk.pack_array(16);
for (int i = 0; i < 16; i++) {
pk.pack_double(mat[i]);
}
}
// this function is looks like de-serializer, taking an msgpack object
// and extracting data from it to the current class fields
void msgpack_unpack(msgpack::object o) {
// check if received structure is an array
if(o.type != msgpack::type::ARRAY) { throw msgpack::type_error(); }
const size_t size = o.via.array.size;
// sanity check
if(size <= 0) return;
// extract value of first array entry to a class field
memcpy(name, o.via.array.ptr[0].via.raw.ptr, o.via.array.ptr[0].via.raw.size);
// sanity check
if(size <= 1) return;
// extract value of second array entry which is array itself:
for (int i = 0; i < 16 ; i++) {
mat[i] = o.via.array.ptr[1].via.array.ptr[i].via.dec;
}
}
// destination of this function is unknown - i've never ran into scenary
// what it was called. some explaination/documentation needed.
template <typename MSGPACK_OBJECT>
void msgpack_object(MSGPACK_OBJECT* o, msgpack::zone* z) const {
}
};