0

I'm trying to make instances of a class, that I can access at anytime, that's the best way I thought to manage this data, if not please say so. I'm trying to store the data of each crypto from binance.us. Here's my class structure:

class CryptoStats {
public:
    std::string name;
    std::string lastPriceStr;
    int lastPrice = 0;
    int score = 0;
    int priceHistory[2] = { 0,0 }; // one column for time, other for price
};

i'd like to know how to make a single instance of it, like CryptoStats["BTCUSD"], where I can then edit it, check if it exists, etc. I tried storing it in a set but it doesn't let me :<

std::set<std::pair<std::string, CryptoStats>> cryptoStats;

basically i'm looking for the C++ equivalent to this Lua table structure:

local cryptoStats = {
["BTCUSD"] = {lastPrice = 0, lastPriceStr = "0", score = 0, priceHistory = {0,0}},
["ETHUSD"] = {lastPrice = 0, lastPriceStr = "0", score = 0, priceHistory = {0,0}}
}
kiner_shah
  • 3,939
  • 7
  • 23
  • 37
JayMeazy
  • 23
  • 4
  • 6
    Looks more like a `std::map cryptoStats;`. https://en.cppreference.com/w/cpp/container/map – mch Nov 16 '21 at 09:51
  • You're probably looking for a `struct` rather than a `class`. – Piotr Siupa Nov 16 '21 at 09:52
  • 1
    @NO_NAME and what's the difference? – Federico Nov 16 '21 at 09:55
  • @Federico https://stackoverflow.com/q/54585/3052438 – Piotr Siupa Nov 16 '21 at 09:55
  • @NO_NAME Is that helpful? Only access priveledges are different and are fully of topic here! – Klaus Nov 16 '21 at 10:14
  • @Klaus Not only that. `struct` allows aggregate initialization by default. (https://en.cppreference.com/w/cpp/language/aggregate_initialization) There are also conceptual differences between structures and classes which are not really enforced by the language but they are still important for programmers. Lua tables allow to access all their members without using specialized member functions. This is how C++ structures are usually treated but classes generally aren't. – Piotr Siupa Nov 16 '21 at 10:22
  • @NO_NAME A class allows also aggregate initialization. There is no difference! The only difference between struct and class is the default of private/public setting. Nothing more! – Klaus Nov 16 '21 at 10:28
  • 1
    @Klaus OK, you're right that classes also support aggregate initialization if all members are public. There is still a conceptual difference, though. Using a class implies an encapsulation and some functions that allows to modify the private members in a controlled way. If you just want a plain non-encapsulated structure and you're using a class instead, you misinform the readers. There is just making a code work and there are also good practices which, while they aren't strictly required, are also important. – Piotr Siupa Nov 16 '21 at 10:43
  • @NO_NAME: If you simply write `class x{public: ...}; you get the same as `struct`. It is only a convention that we make it more readable to users of our code to use sruct, if we want to tell that all and everything can be accessed without any restriction. But this is not a language feature, that is programmers brain support :-) – Klaus Nov 16 '21 at 10:59
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/239273/discussion-between-no-name-and-klaus). – Piotr Siupa Nov 16 '21 at 11:19

1 Answers1

1

You have the coise to use a std::map where in your case name is not longer part of your class. But you also can use a set which contains the name inside the class and use it to find it from your container.

The following code provides both alternatives:

class CryptoStatsWithoutName {
public:
    std::string lastPriceStr;
    int lastPrice = 0;
    int score = 0;
    int priceHistory[2] = { 0,0 }; // one column for time, other for price
};

class CryptoStats{
public:
    std::string name;
    std::string lastPriceStr;
    int lastPrice = 0;
    int score = 0;
    int priceHistory[2] = { 0,0 }; // one column for time, other for price

    // a set is always be sorted, as this, it needs for user defined types
    // a comparison operator
    bool operator<( const CryptoStats& outer) const
    {   
        return name< outer.name;
    }   
};

// if we want to use std::set<>::find we have to provide additional comparison operators
// if the key we search for is not the stored user defined type
bool operator<( const CryptoStats& cs, const std::string& key) { return cs.name < key; }
bool operator<( const std::string& key, const CryptoStats& cs) { return key < cs.name; }


int main()
{   
    std::map<std::string, CryptoStatsWithoutName> cryptoStatsWithoutName=
    {   
        {"BTCUSD", {"1", 1, 1,{1,1}}},
        {"ETHUSD", {"2" ,2, 2, {2,2}}}
    };
    
// attention: if the key is not found, a new feault initialized element will be inserted into the map!
    std::cout << cryptoStatsWithoutName[ "BTCUSD" ].lastPrice << std::endl;
    
    std::set< CryptoStats, std::less<> > cryptoStats =
    {   
        {"BTCUSD", "1", 1, 1,{1,1}},
        {"ETHUSD", "2" ,2, 2,{2,2}}
    };
    
    // attention: If key is not found you access `end()` which will crash your prog. Better you check that your returned iterator is valid!
    std::cout << cryptoStats.find("ETHUSD")->lastPrice << std::endl;
}

Attention: If you search for a non existing key, the code will fail. In case of a map it will insert a new default initialized element with the given search key, which is not intended I believe. In the case with std::find you better have to check if find with return end() before accessing the returned data. In the last case your program may throw an exception or terminate by accessing non existent data.

Klaus
  • 24,205
  • 7
  • 58
  • 113