I've developed a recent interest in (re-)learning programming, so I have taken up C++ as it is a commonly used language. However, I've ran into a roadblock, and I have doubts whether my solution is the best way to go around it.
I have a relatively complex class (for me anyways), with around 20 variables, which have been divided into 4 groups for simplification. It also has a parent class which is called during object initialization.
However, I do not have a need to set these to values other than their default values in all objects, so I have set-up various different constructor overloads to account for all possible combinations (8 constructors in total). Therefore, in order to prevent writing repetitive code, I have written an handful of private functions, only called during the constructor, that set variables to the value I assign when creating a new object.
Is this the optimal way of solving this problem? I have also thought of grouping these variables into classes or structs, but it just feels like that's needlessly complicated, when calling the relevant functions during the various constructor overloads should do the trick. If this is not optimal, what would be the best way of solving this? And why?
I can provide with a more detailed description of my problem, but it'd be a pretty big wall of text (I had first written that one up, but it got way too out of hand). Thank you in advance for your input.
As requested, here is the class definition (Weapon). The parent class (Item) is already defined and working as intended, so I will not paste it, so people will not have to read a massive wall of text.
Weapon class definition:
class Weapon: public Item {
public:
// Default constructor
Weapon();
// Full constructor
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short StartEnergy, unsigned short MaxEnergy);
// Constructor for Weapons without Cooldown System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short CurrentEnergy, unsigned short MaxEnergy);
// Constructor for Weapons without Reload System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short CurrentEnergy, unsigned short MaxEnergy);
// Constuctor for Weapons without Energy System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition);
// Constructor for Weapons without Cooldown nor Reload System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short CurrentEnergy, unsigned short MaxEnergy);
// Constructor for Weapons without Cooldown nor Energy System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition);
// Constructor for Weapons without Reload nor Energy System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short MaxMagazine, unsigned short MaxAmmunition);
// Constructor for Weapons without Cooldown, Reload nor Energy System
Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short maxMagazine, unsigned short MaxAmmunition);
~Weapon();
void m_print();
/*Edited public get and set functions for each variable as they are not relevant*/
private:
// Ubiquitous variables
unsigned short WepGenericID = 0;
unsigned short WepVariantID = 0;
unsigned short WepSkinID = 0;
double EquipLoad = 0;
double EquipLoadperAmmo = 0;
unsigned short ModesNo = 1;
Mode* pModes = NULL;
unsigned short MaxAmmunition = 0;
unsigned short CurrentAmmunition = 0;
unsigned short MaxMagazine = 0;
unsigned short CurrentMagazine = 0;
// Cooldown System variables
bool WeaponCooldown = false;
unsigned short CooldownType = 0;
double CooldownDuration = 0;
unsigned short CooldownAction = 0;
double CooldownPeriod = 0;
// Reload System variables
unsigned short ReloadType = 0;
unsigned short ReloadStyle = 0;
double ReloadTime = 0;
// Energy System variables
unsigned short CurrentEnergy = 0;
unsigned short MaxEnergy = 0;
//Constructor Auxiliary Functions
void m_setGeneralWeapon(double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short MaxMagazine, unsigned short MaxAmmunition);
void m_setCooldownSystem(unsigned short CooldownType, double CooldownDuration, unsigned short CooldownAction, double CooldownPeriod);
void m_setReloadSystem(unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime);
void m_setEnergySystem(unsigned short StartEnergy, unsigned short MaxEnergy);
void m_setWeaponIDs();
void m_WepNameDecisionTree();
string m_searchName();
};
Item parent class definition
class Item {
public:
Item();
Item(unsigned GenericID);
Item(unsigned GenericID, bool NameFlag);
~Item();
void m_setCustomName();
private:
unsigned GenericID = 0;
unsigned short GenCategoryID = 0;
unsigned short GenSubCategoryID = 0;
bool NameFlag = false;
string ItemName = "Missingno";
unsigned long InstanceID = 0;
};
make seperate classes for your subsystems.
create your weapons using a builder/factory pattern:
How to implement the factory method pattern in C++ correctly
https://en.wikibooks.org/wiki/C%2B%2B_Programming/Code/Design_Patterns/Creational_Patterns
you could also seperate the ammo, leaving you with only few actual members. This way you can build everything with a more modular approach, giving you the possibility to easier extend or modify your functionality
One problem I see with the Weapon API as posted is that the constructors take a lot of loosely-typed arguments, which makes the code that uses them hard to understand and verify. For example, say you (or your fellow developer) have used your constructor API to add this line to your codebase:
Weapon bfg(id, true, 3.0, 5.0, 6, modesPtr, COOLDOWN_QUICK, 5.0, 3, 4, RELOAD_SLOW, RELOAD_ANYTIME, 3.8, 12, 14);
Reading that line, it's very hard to see what most of the numbers mean. If an argument was accidentally omitted, or the ordering of two of the arguments was reversed, it will likely not be obvious (either you or to the compiler) that there is a bug there; instead, you might not find out about the error until some play tester (or customer?) files a bug report, which is an expensive and time-consuming way to discover errors.
Therefore, I recommend reducing the number of arguments you have to pass to the constructor to as few as you can get away with. For example, another way to write the above might be this:
Weapon bfg(id, true);
bfg.SetEquipLoad(3.0);
bfg.SetEquipLoadPerAmmo(5.0);
bfg.SetModes(6, modesPtr);
bfg.SetCooldownType(COOLDOWN_QUICK);
[...]
Admittedly that's a lot more verbose (and it does make it possible to forget to set something you should have set), but at least when you look at it, it's very obvious that 3.0 applies to the EquipLoad setting and 5.0 applies to the EquipLoadPerAmmo setting, and not the other way around. i.e. you don't have to constantly look back and forth between your .h file and the code to try and figure out what each value is referring to.
Note that each set-method should take all of the parameters needed to yield a useful result; so for example if it doesn't make any sense to specify an EquipLoad without also specifying EquipLoadPerAmmo, then you might as well have both of those set by a single call instead:
bfg.SetEquipLoadAndEquipLoadPerAmmo(3.0, 5.0);
... so that it becomes impossible (i.e. a compile-time error) for a coder to make the mistake of setting one but neglecting to set the other.
As for handling the verboseness to minimize redundant code, the next step would be to wrap the above code inside a function, so that for any given weapon-type there is only one place that creates it, e.g. :
Weapon MakeBFG(unsigned id)
{
Weapon bfg(id, true);
bfg.SetEquipLoad(3.0);
bfg.SetEquipLoadPerAmmo(5.0);
bfg.SetModes(6, modesPtr);
bfg.SetCooldownType(COOLDOWN_QUICK);
[...]
return bfg;
}
Now the rest of your code can just call Weapon bfg = MakeBFG(idCounter++); whenever it wants to create a new BFG gun.
Outside of that, I agree with the other poster -- your class seems to be handling a number of different things, and if you can find a way to decompose it into multiple smaller classes (not all of which need to be exposed via the public API; private classes are great), that would probably help you manage the overall complexity of the code; doing so is especially beneficial if you think you will want to continue to add new features/behaviors in the future, since if you keep everything together in a single class, the complexity of that class will quickly get out of hand as you try to make it support more and more different behaviors/use-cases.
When unsigned/signed long int a; is possible
why unsigned/signed long float/double a; is not possible ?
Why do I get too many types in declaration error for the latter and not for the former ?
There are three floating point types: float, double and long double. None of these have unsigned equivalents, so putting signed or unsigned in front of them is not valid. There is no such type as long float.
You are getting that message because a long double exists, but an unsigned long double does not. unsigned can also be interpreted as an int, therefore you possess two types in the latter declaration: unsigned and long double. I do not believe there is a long float in C++.
That is because the first (long int) is a documented variable type, while the second isn't.
The data types that the C++ language supports are:
char
unsigned char
signed char
int
unsigned int
signed int
short int
unsigned short int
signed short int
long int
signed long int
unsigned long int
float
double
long double
What does a "limb" refer to in the domain of arbitrary precision integer?
In the GNU Multiple Precision Arithmetic Library (GMP), as mentioned in comments, is the largest integer word available:
#ifdef __GMP_SHORT_LIMB
typedef unsigned int mp_limb_t;
typedef int mp_limb_signed_t;
#else
#ifdef _LONG_LONG_LIMB
typedef unsigned long long int mp_limb_t;
typedef long long int mp_limb_signed_t;
#else
typedef unsigned long int mp_limb_t;
typedef long int mp_limb_signed_t;
#endif
#endif
typedef unsigned long int mp_bitcnt_t;
typedef struct
{
int _mp_alloc; /* Number of *limbs* allocated and pointed to by the _mp_d field. */
int _mp_size; /* abs(_mp_size) is the number of limbs the last field points to. If _mp_size is negative this is a negative number. */
mp_limb_t *_mp_d; /* Pointer to the limbs. */
} __mpz_struct;
...
typedef __mpz_struct mpz_t[1];
So a limb can be a unsigned int, unsigned long int, or unsigned long long int, depending on the underlying architecture.
GMP then uses multiple limbs to store and calculate multiple precision integers, by applying a very efficient implementation of machine-specific integer code with highly optimized algorithms for multiple-precision arithmetic. The reason to use machine unsigned integers for these calculations is because integer arithmetic is simple, fast, and very realiable, whereas floating-point arithmetic and signed integer arithmetic are not as nearly standarized and portable as unsigned integer arithmetic.
Go easy on me, I'm still a newb with C/C++.. I know this has been asked a few times, and I've tried following the solutions given to no avail. This code is for a NetBurner processor, DWORD is 32 bit unsigned, WORD is 16 bit unsigned.
header func.h:
class funcs
{
// ...
private:
void myfunc();
WORD data001;
DWORD data002[100];
DWORD data003[100];
// ...
}
I have this function that calls upon that data in my class, funcs.cpp. Assume all variables have been initialized:
void funcs::myfunc()
{
data001++;
data002[data001] = x; // random x for this example
data003[data001] = y;
}
My compiler is complaining: "error: invalid types 'DWORD[WORD] for array subscript". I've changed the array subscript type to "int", "unsigned int" and every other type I could think of, and still get the error. I tried the solutions given in previous posts:
void funcs::myfunc()
{
data001++;
this->data002[data001] = x; // random x for this example
this->data003[data001] = y;
}
but it was to no avail. I've also tried containing myfunc definition within the class, same error. Any ideas/solutions? I'm stumped. Thanks guys!!
Edit: data types provided in a header file:
typedef unsigned char BOOL;
typedef unsigned char BOOLEAN;
typedef unsigned char BYTE; /* Unsigned 8 bit quantity */
typedef signed short SHORT;/* Signed 16 bit quantity */
typedef unsigned short WORD; /* Unsigned 16 bit quantity */
typedef unsigned long DWORD;/* Unsigned 32 bit quantity */
typedef signed long LONG; /* Signed 32 bit quantity */
typedef volatile unsigned char VBOOLEAN;
typedef volatile unsigned char VBYTE; /* Unsigned 8 bit quantity */
typedef volatile short VSHORT; /* Signed 16 bit quantity */
typedef volatile unsigned short VWORD; /* Unsigned 16 bit quantity */
typedef volatile unsigned long VDWORD; /* Unsigned 32 bit quantity */
typedef volatile signed long VLONG; /* Signed 32 bit quantity */
Screenshot:
Your real code (transcribed from the screenshot) is:
DWORD u_data002;
WORD u_data003;
u_data002[u_data_003] = whatever;
which tries to index an integer as if it were an array or pointer.
Presumably, either u_data002 is supposed to be an array, or you meant to write something other than u_data002.
Which are variable by " Uint "? is that there are " Uint8 ", " Uint16 ", etc ...
But what are they ?
Now I have some time using C ++ but I have never needed to use these variables and cause me curious.
Thanks in advance.
uint is not a standard type. On some system uint is typedefed as
typedef unsigned int uint ;
uint is not a basic data type as mentioned in the standard. Sometimes, to make a variable to have constant size across platform [to enable portability], some typedefs are used.
You can have a look at cstdint header for more info.
With best assumption, uint should be a typedef to uint32_t
typedef uint32_t uint;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed long int32_t;
It can help you..
if you are dealing with variable base on it's size then you can declare it like this