I have written a function in c++ which receives a struct as a input. The struct object received has two arrays. I need to use both the arrays for different purposes. The array names have been created in a certain format. How to retrieve array names in a string.
struct INFO
{
float fADataLHS[3] = {1,2,3};
float fADataRHS[3] = {4,5,6};
Struct INFO has been defined where two arrays have been defined an initialized. The function useStruct uses both the function for different purposes.
void useStruct(struct *INFO)
{
--------;
--------;
}
int main()
{
struct INFO info;
useStruct(info);
}
I want a method in which I can retrieve the name of the array as for ex. fAdataLHS and store it to a string. The idea is to find the sub-string LHS and RHS from the string names and process then accordingly.
PS: I am quite new to c++.
I will go simple as you're a begginer to C++.
If you want to use both of arrays for different purposes, just doit. For instance:
void use_array_for_different_purposes(INFO *info)
{
// Purpose one, printing values using fADataLHS.
for (int i = 0; i < 3; i++) {cout << info->fADataLHS[i] << endl;}
// Purpose two, computing total sum using fADataRHS.
int acum;
for (int i = 0; i < 3; i++) {acum += info->fADataRHS[i];}
}
As you can see, you don't need to get the arrays names as strings values.
If I understand corectly, your use case is this: you have two (or more) names and each has a float array associated with it. You want to get the array by name and process the data.
Consider this code:
class INFO
{
std::map<std::string, std::vector<float>> vectors;
public:
INFO() : vectors{}
{
vectors["fADataLHS"] = { 1, 2, 3 };
vectors["fADataRHS"] = { 4, 5, 6 };
}
const std::vector<float>& operator[](const std::string& key) const // access vector by key
{
return vectors.at(key);
}
};
void useStruct(const INFO& info) // pass instance by const reference
{
std::cout << info["fADataLHS"][0] << "\n"; // access element 0 from the fADataLHS array
// get the entire array:
const auto& arr = info["fADataRHS"];
// this will throw a std::out_of_bounds
const auto& arr = info["non-existent-key"];
}
EDIT: A few other notes:
in C++ try not to use float - use double instead
if you need to alter the vector contents from client code, add a non-const version of the operator[]
Related
There is a class say Person which has member variables name, email, mobile, gender.
Now, we are getting this information in an array.
std::vector<std::string> a[] = {"XYZ", "xyz#mail.com", "1234567890", "Male"};
Person p;
Now, instead of writing it like:
p.name = a[0]; p.email = a[1]....
I want something like this for dynamic allocation as well as for reducing code lines:-
std::vector<std::string> b[] = {"name", "email", "mobile", "gender"};
int len = a.size();
for (int i=0; i < len ; i++)
{
set_value(p, b[i], a[i]);
}
How to write a function like set_value, or is there any way to do something like this in c++ ?
Right now in above example we have a vector size of 4, but it can be in size of 10 or 50 or more also. In that case if we don't have the way of setting the values dynamically then we might have to write same number of lines for setting every member variable .. .?
I think one of the solutions in which you are talking about is to implement all the fields of the Person class as a map<K, V>, which will store all of yours properties. In this case you will be able to refer to the value by the key of a map.
I would not recommend this solution for you. You can think of set_value function as a way in which the names from array a should refer to the name of the properties or rather how does those names should be linked to the functions which could invoke a proper property-right set method.
If those setting functions you are interested in use the same base type as a parameter, you could create map of strings and function pointers.
#include <map>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
class Person{
std::map<std::string, std::string> _properties;
public:
Person() = default;
~Person() = default;
void set_values(vector<string> & fields, vector<string> & values){
if(fields.size() != values.size()) return;
for(int i = 0; i < fields.size(); ++i){
_properties.insert_or_assign(fields[i], values[i]);
}
}
void print(){
for (auto const &it : _properties)
std::cout << it.first << " => " << it.second << '\n';
}
};
int main(){
Person p;
vector<string> fields = {"email", "name"};
vector<string> values = {"a#b.com", "Andrew"};
p.set_values(fields, values);
p.print();
vector<string> fields2 = {"name"};
vector<string> values2 = {"Tom"};
p.set_values(fields2, values2);
p.print();
return 0;
}
This minimal working example shows what you want to get and it has a lot of constraints, such as it assumes that all of the values are stored as a std::string which can not be a good in every case. It produces a following output after applying the first set of fields and values:
email => a#b.com
name => Andrew
After the second one, the email stays the same but name is updated as follows:
email => a#b.com
name => Tom
I've compiled it using g++ version 7.1 using using following command:
g++ --std=c++1z main.cpp on my Fedora 26.
Well, there are no easy way to set a variable by name... There are library that allows to do something like that (for ex. boost fusion) but in practice, you could also write a simple function if you have only a few functions to write on a few classes.
One simple possibility assuming you want to reuse the code that fill a object:
void set_values_of(Person &p, const std::vector<std::string> &data)
{
assert(data.size() == 4); // put whatever error handling you want...
p.name = data[0];
p.email = data[1];
p.mobile = data[2];
p.gender = data[3];
}
And if you prefer, you might change second argument for another source (for ex. a stream or one line a string). At that point, it really depends on your application.
If your prefer immuable objects, you might also consider having a factory free function:
Person create_from(const std::vector<std::string> &data) { ... }
I'm trying to implement Extendible Hashing in C++
There's a struct which acts as an Index and it contains an array of type 'Bucket'
Bucket * bucket_pointers;
There's another struct, Bucket, which has an array, which holds my values
E values[N] = {};
I've got a more or less working program, with one problem: Everytime I to double the size of my hash table, I'm copying all of my buckets into a new array (twice the size)
Index_0
Bucket <n= 3, local_depth=2, 0x100200000>
[12,4,,8,]
Index_1
Bucket <n= 0, local_depth=1, 0x100200028>
[,,,,]
Index_2
Bucket <n= 3, local_depth=2, 0x100200050>
[2,10,6,,]
Index_3
Bucket <n= 0, local_depth=1, 0x100200078>
[,,,,]
However, the Bucket with address 0x100200078 should actually point to the bucket with address 0x100200028, i.e. both indices (1 and 3) should point to the same bucket.
Here I'm deciding whether to split a bucket or double the size of my index...
while (!bucket_pointers[h%index_size].append(e)){
if(bucket_pointers[h%index_size].local_depth<global_depth){
split(hashValue);
}
else if(bucket_pointers[h%index_size].local_depth==global_depth){
resize();
}
}
I'm currently doubling the size of my array like this:
for (size_t i = 0; i < index_size; ++i){
for (size_t j = 0; j < bucket_pointers[i].n; ++j){
newBucket_pointers[i] = bucket_pointers[i];
newBucket_pointers[i+index_size] = bucket_pointers[i];
}
}
Note that Bucket * bucket_pointers; is not an array of Bucket pointers as it's name would imply. It's a pointer to a Bucket (the first Bucket in an array of Buckets to be specific).
So, when you copy the array of buckets to another, you end up with identical copies of buckets each with their own values arrays.
newBucket_pointers[i] = bucket_pointers[i];
newBucket_pointers[i+index_size] = bucket_pointers[i];
If you want newBucket_pointers[i] and newBucket_pointers[i+index_size] to be pointers that point to the same Bucket then the type of bucket_pointers (and newBucket_pointers) should actually be Bucket**. Then bucket_pointers is a pointer to a Bucket* and bucket_pointers[i] is a pointer to a Bucket. That way bucket_pointers[i], newBucket_pointers[i] and newBucket_pointers[i+index_size] would point to the same Bucket. I recommend a std::vector<Bucket*> bucket_pointers instead though for easier memory management.
If instead, you intend to copy the Buckets as you do now but have their values member point to a shared array, then you can keep bucket_pointers as it is and you need to change the type of values to a pointer and allocate the array separately. If you want to share the array this way, you should probably use a shared_ptr to make the eventual deallocation easier.
I've included some code below that performs as a very simple hash table. It is for instructional purpose only and not robust enough for use in a real application. In real life use the built-in std::unordered_set which works much better.
I avoid the need to change the bucket size, by using a linked list as a bucket that can expand as needed.
Is this example helpful to set you on the right track?
#include <iostream>
#include <array>
#include <list>
#include <string>
#include <cassert>
class CTable
{
public:
void Add(const std::string &sKey, int nVal);
int Find(const std::string &sKey);
protected:
size_t Index(const std::string &sKey);
private:
struct SData
{
SData(const std::string &s, int n)
: sKey(s)
, nVal(n)
{
}
std::string sKey;
int nVal;
};
typedef std::list<SData> Bucket_t;
enum { nBuckets = 24 };
typedef std::array<Bucket_t, nBuckets> Table_t;
Table_t m_table;
const SData *Lookup(const Bucket_t &b, const std::string &sKey);
};
void CTable::Add(const std::string &sKey, int nVal)
{
size_t nIndex = Index(sKey);
const SData *p = Lookup(m_table.at(nIndex), sKey);
if (p)
throw std::runtime_error("duplicate key");
m_table.at(nIndex).push_back(SData(sKey, nVal));
}
int CTable::Find(const std::string &sKey)
{
size_t nIndex = Index(sKey);
const SData *p = Lookup(m_table.at(nIndex), sKey);
if (p)
return p->nVal;
else
throw std::runtime_error("not found");
}
size_t CTable::Index(const std::string &sKey)
{
return std::hash<std::string>()(sKey) % m_table.size();
}
const CTable::SData *CTable::Lookup(const CTable::Bucket_t &b,
const std::string &sKey)
{
for (const SData &s : b)
if (s.sKey == sKey)
return &s;
return nullptr;
}
int main()
{
CTable t;
t.Add("one", 1);
t.Add("two", 2);
t.Add("three", 3);
assert(2 == t.Find("two"));
try
{
t.Find("four");
assert(false);
}
catch (std::exception &)
{
}
try
{
t.Add("two", 3);
assert(false);
}
catch (std::exception &)
{
}
return 0;
}
As #user2079303 already pointed out, what you want is an array of Bucket**.
Let me clarify this with some imagery:
Extendible-hashing explained
One thing to remember in case Bucket** index = new Bucket*[<size_here>] confuses you,
say you want to make a simple int-array.
You would do:
int* nums = new int[5];
Simply imagine to decrease the number of *-symbols on the right-side since
that is defining what the content-type shall be. And so all you want to store is addresses to Buckets. Thus the index containing 1 or more pointer to Buckets.
Hope it helps!
I am currently working on an dynamic memory container.
Basic idea of the class is that you should be able to get the iterator of an object if you really do not know it, without the use of a for loop throughout all the elements to boost performance. The issue I have is the following; when you pass your pointer address to the object you want to get the iterator of it type casts the object into the extended memory containers structures type. This type contains an extra element, an integer. (IteratorNum)
When following the code the integer within the function is set to correct value, as below would be 50. But when the returned value is set into the local integer used in the main function it is 200? I've been adding watches and cannot figure out how it is possible that the function returns 50 but value gets set to 200.
template <typename DataType> class MemoryContainer {
public:
struct LevelData : DataType
{
int element;
};
DataType &New()
{
elements++;
//Reallocate the size of the array
ld = (LevelData*)realloc(ld, sizeof(LevelData) * elements);
//Set the iteratorNumber
ld[elements - 1].element = elements - 1;
return ld[elements - 1];
}
DataType *reserve(int num)
{
return calloc(num, sizeof(DataType));
}
DataType &operator[](int i)
{
return ld[i];
}
bool inArray(DataType *type)
{
//Compare memory addresses and see if it's within.
return (type >= &ld[0]) && (type < &ld[elements - 1]);
}
static unsigned int getIterator(DataType *type)
{
// v this is 50, but in main says returns 200.
return ((LevelData*)type)->element;
}
MemoryContainer()
{
elements = 0;
}
~MemoryContainer()
{
free(data);
}
private:
unsigned int elements;
LevelData *ld;
};
struct Effective
{
//Set it to polymorphic classes
virtual void dummy()
{
}
char * testvar;
Effective(char * c)
{
testvar = c;
}
Effective(){}
};
MemoryContainer<Effective> myContainer;
int _tmain(int argc, _TCHAR* argv[])
{
//Create 200 elements in the array
for(int i = 0; i < 200; i++)
myContainer.New().testvar = "E";
//Add pointer for testing purposes to get the iterator.
Effective * pointer = &myContainer[50];
//Test setting it's value
pointer->testvar = "HEHEHE";
//Get iterator of our pointer in the array
unsigned int i = myContainer.getIterator(pointer);
printf(pointer->testvar);
system("PAUSE");
return 0;
}
I suspect it is the visual studio debugger getting confused between your two i variables. If you print out the value of i, it will print correctly. If you change the name of your variable to something else, the value shows as 50 in the debugger.
That said, your code is a mish-mash of c and c++ and won't work correctly with anything that requires a copy constructor. I would suggest at the very least using new [] rather than realloc.
Also, any user of this collection who tries to store a class with a member variable called element is going to get mighty confused.
The unsigned int i in the main function really has a value of 50, but the debugger is confusing it with the i declared in the for loop (I reproduced this with Visual Studio 2013). If you cout i it will be 50, and if you change the variable name it will show up as 50 in the debugger. I've never seen this problem before so I wonder if it might be due to your use of malloc/realloc/free with C++ objects.
How do I initialize an array of objects from a function? I'm aware the code below is impractical; I'm just teaching myself C++.
Here is a structure that contains data.
struct pointStruct {
int numberPoints;
Point2D pointArray;
};
The Point2D class has instance variables x and y. In a separate function, I have:
void setPoints(void) {
pointStruct myPointData;
myPointData.numberPoints = 4;
myPointData.pointArray[4]; // here is the problem
// loop with i
myPointData.pointArray[i].x = ...;
myPointData.pointArray[i].y = ...;
}
I'm trying to initialize the array so that I can loop through it and set the x,y coordinates. I've tried using new and some other methods but I can't work through what I need to do. How can I fix this?
When I try to compile this code, I get the error "no match for 'operator[]' in 'myPointData.pointStruct::pointArray[4]' "
You should probably use std::vector like MadScienceDreams suggests.
However, if you want to learn about such things, you could use a pointer instead. For example:
struct pointStruct {
int numberPoints;
Point2D* pointArray;
};
void setPoints(void) {
pointStruct myPointData;
const int num_points = 4;
myPointData.numberPoints = num_points;
myPointData.pointArray = new Point2D[num_points];
for(int i = 0; i < num_points; ++i) {
myPointData.pointArray[i].x = ...;
myPointData.pointArray[i].y = ...;
}
// Do stuff with myPointData...
// Don't forget to have a "delete" for every "new" when you're done.
delete[] myPointData.pointArray;
}
Point2D pointArray;
pointArray is a single instance to Point2D. It is not an array of instances in which case it's type is Point2D [N].
myPointData.pointArray[4];
The above statement calls operator [] taking a parameter of type int, which is not you actually want. Since there is no such member function in Point2D, compiler complains. If you wish to create array of instances, use std::vector<Point2D>.
I have a pretty standard class with some public member functions and private variables.
My problem originally stems from not being able to dynamically name object instances of my class so I created an array of pointers of the class type:
static CShape* shapeDB[dbSize];
I have some prompts to get info for the fields to be passed to the constructor (this seems to work):
shapeDB[CShape::openSlot] = new CShape(iParam1,sParam1,sParam2);
openSlot increments properly so if I were to create another CShape object, it would have the next pointer pointing to it. This next bit of code doesn't work and crashes consistently:
cout << shapeDB[2]->getName() << " has a surface area of: " << shapeDB[2]->getSA() << shapeDB[2]->getUnits() << endl;
The array of pointers is declared globally outside of main and the get() functions are public within the class returning strings or integers. I'm not sure what I'm doing wrong but something relating to the pointer set up I'm sure. I'm writing this code to try and learn more about classes/pointers and have gotten seriously stumped as I can't find anyone else trying to do this.
I'm also curious as to what the CShape new instances get named..? if there is any other way to dynamically create object instances and track the names so as to be able to access them for member functions, I'm all ears.
I've tried all sorts of permutations of pointer referencing/de-referencing but most are unable to compile. I can post larger chunks or all of the code if anyone thinks that will help.
class CShape {
int dim[maxFaces];
int faces;
string units;
string type;
string name;
bool initialized;
int slot;
public:
static int openSlot;
CShape();
CShape(int, string, string); // faces, units, name
~CShape();
void initialize(void);
// external assist functions
int getA(void) {
return 0;
}
int getSA(void) {
int tempSA = 0;
// initialize if not
if(initialized == false) {
initialize();
}
// if initialized, calculate SA
if(initialized == true) {
for(int i = 0; i < faces; i++)
{
tempSA += dim[i];
}
return(tempSA);
}
return 0;
}
string getUnits(void) {
return(units);
}
string getName(void) {
return(name);
}
// friend functions
friend int printDetails(string);
};
// constructor with values
CShape::CShape(int f, string u, string n) {
initialized = false;
faces = f;
units = u;
name = n;
slot = openSlot;
openSlot++;
}
My guess is you use the CShape constructor to increment CShape::openSlot?
You're probably changing the value before it's read, thus the pointer is stored in a different location.
Try replacing openSlot with a fixed value to rule out this CShape::option.
-- code was added --
I'm pretty sure this is the problem, the constructor is executed before the asignment, which means the lhs. will be evaluated after CShape::openSlot is incremented.