Coming from a Java background, I find C++'s enums very lame. I wanted to know how to write Java-like enums (the ones in which the enum values are objects, and can have attributes and methods) in C++.
For example, translate the following Java code (a part of it, sufficient to demonstrate the technique) to C++ :
public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
private double mass() { return mass; }
private double radius() { return radius; }
// universal gravitational constant (m3 kg-1 s-2)
public static final double G = 6.67300E-11;
double surfaceGravity() {
return G * mass / (radius * radius);
}
double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Usage: java Planet <earth_weight>");
System.exit(-1);
}
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight/EARTH.surfaceGravity();
for (Planet p : Planet.values())
System.out.printf("Your weight on %s is %f%n",
p, p.surfaceWeight(mass));
}
}
Any help would be greatly appreciated!
Thanks!
we should do the following steps to have an enum with different values: Create enum constructor which accepts multiple values. Assign each constructor argument to a member field in the enum definition. Create getter methods so we can access any of the values assigned to a particular enum constant.
I must answer a resounding no because actually you can't. Enums have their own data type and each enum is essentially a new data type.
Enum Class in Java An enum class can include methods and fields just like regular classes. When we create an enum class, the compiler will create instances (objects) of each enum constants. Also, all enum constant is always public static final by default.
java file may have only one public class. You can therefore declare only one public enum in a . java file. You may declare any number of package-private enums.
One way to simulate Java enums is to create a class with a private constructor that instantiates copies of itself as static variables:
class Planet {
public:
// Enum value DECLARATIONS - they are defined later
static const Planet MERCURY;
static const Planet VENUS;
// ...
private:
double mass; // in kilograms
double radius; // in meters
private:
Planet(double mass, double radius) {
this->mass = mass;
this->radius = radius;
}
public:
// Properties and methods go here
};
// Enum value DEFINITIONS
// The initialization occurs in the scope of the class,
// so the private Planet constructor can be used.
const Planet Planet::MERCURY = Planet(3.303e+23, 2.4397e6);
const Planet Planet::VENUS = Planet(4.869e+24, 6.0518e6);
// ...
Then you can use the enums like this:
double gravityOnMercury = Planet::MERCURY.SurfaceGravity();
With C++11's introduction of constexpr
. There's yet another way to implement typed enums. One that works practically the same as normal enums (is stored as an int
variable and can be used in a switch
statement), but also allows them to have member functions.
In the header file you would put:
class Planet {
int index;
public:
static constexpr int length() {return 8;}
Planet() : index(0) {}
constexpr explicit Planet(int index) : index(index) {}
constexpr operator int() const { return index; }
double mass() const;
double radius() const;
double surfaceGravity() const;
};
constexpr Planet PLANET_MERCURY(0);
constexpr Planet PLANET_VENUS(1);
constexpr Planet PLANET_EARTH(2);
// etc.
And in the source file:
static double G = 6.67300E-11;
double Planet::mass() {
switch(index) {
case PLANET_MERCURY: return 3.303e+23;
case PLANET_VENUS: return 4.869e+24;
case PLANET_EARTH: return 5.976e+24;
// Etc.
}
}
double Planet::radius() {
// Similar to mass.
}
double Planet::surfaceGravity() {
return G * mass() / (radius() * radius());
}
Which can then be used as:
double gravityOnMercury = PLANET_MERCURY.SurfaceGravity();
Unfortunately, the enum entries cannot be defined as static constants within the class body. They must be initialized upon declaration, because they are constexpr
, but inside the class, the class is not yet a complete type and thus cannot be instantiated.
May be this is what you want --
#include<iostream>
using namespace std;
class Planet {
double mass,radius;
Planet(double m, double r) : mass(m) : radius(r) {}
public:
static const Planet MERCURY;
void show(){
cout<<mass<<","<<radius<<endl;
}
} ;
const Planet Planet::MERCURY = Planet(1.0,1.2);
int main(){
Planet p = Planet::MERCURY;
p.show();
}
This is just a small code, Im sure you can modify this to suit your needs..
This is ugly, verbose, and generally a dumb way to go. But I figured I'd post a complete code example by way of explanation. For extra points it's actually possible to define a compile-time expanded iteration over the solar planets by tweaking the template specializations just a tad.
#include <string>
#include <sstream>
#include <iostream>
#include <cstdlib>
class Planet {
public:
static const double G = 6.67300E-11;
Planet(const ::std::string &name, double mass, double radius)
: name_(name), mass_(mass), radius_(radius)
{}
const ::std::string &name() const { return name_; }
double surfaceGravity() const {
return G * mass_ / (radius_ * radius_);
}
double surfaceWeight(double otherMass) const {
return otherMass * surfaceGravity();
}
private:
const ::std::string name_;
const double mass_;
const double radius_;
};
enum SolarPlanets {
MERCURY,
VENUS,
EARTH,
MARS,
JUPITER,
SATURN,
URANUS,
NEPTUNE
};
template <SolarPlanets planet>
class SolarPlanet : public Planet {
};
template <>
class SolarPlanet<MERCURY> : public Planet {
public:
SolarPlanet() : Planet("MERCURY", 3.303e+23, 2.4397e6) {}
};
template <>
class SolarPlanet<VENUS> : public Planet {
public:
SolarPlanet() : Planet("VENUS", 4.869e+24, 6.0518e6) {}
};
template <>
class SolarPlanet<EARTH> : public Planet {
public:
SolarPlanet() : Planet("EARTH", 5.976e+24, 6.37814e6) {}
};
template <>
class SolarPlanet<MARS> : public Planet {
public:
SolarPlanet() : Planet("MARS", 6.421e+23, 3.3972e6) {}
};
template <>
class SolarPlanet<JUPITER> : public Planet {
public:
SolarPlanet() : Planet("JUPITER", 1.9e+27, 7.1492e7 ) {}
};
template <>
class SolarPlanet<SATURN> : public Planet {
public:
SolarPlanet() : Planet("SATURN", 5.688e+26, 6.0268e7) {}
};
template <>
class SolarPlanet<URANUS> : public Planet {
public:
SolarPlanet() : Planet("URANUS", 8.686e+25, 2.5559e7) {}
};
template <>
class SolarPlanet<NEPTUNE> : public Planet {
public:
SolarPlanet() : Planet("NEPTUNE", 1.024e+26, 2.4746e7) {}
};
void printTerranWeightOnPlanet(
::std::ostream &os, double terran_mass, const Planet &p
)
{
const double mass = terran_mass / SolarPlanet<EARTH>().surfaceGravity();
os << "Your weight on " << p.name() << " is " << p.surfaceWeight(mass) << '\n';
}
int main(int argc, const char *argv[])
{
if (argc != 2) {
::std::cerr << "Usage: " << argv[0] << " <earth_weight>\n";
return 1;
}
const double earthweight = ::std::atof(argv[1]);
printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<MERCURY>());
printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<VENUS>());
printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<EARTH>());
printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<MARS>());
printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<JUPITER>());
printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<SATURN>());
printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<URANUS>());
printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<NEPTUNE>());
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With