Automagically create a vector of defined objects? - c++

Assume I have a macro that makes a class member in c++. Is there a way to modify the below example to automatically create NumbersILike::nums? Assume the objects created aren't the same sizeof.
#define MAKE_NUM(num) int num
class NumbersILike
{
MAKE_NUM(three);
MAKE_NUM(four);
MAKE_NUM(five);
std::vector<int*> nums = { &three,&four,&five };
};

It's possible. I looked up 'X Macro'.
class NumbersILike
{
public:
#define LIST_OF_ITEMS \
X(three) \
X(four) \
X(five)
#define X(nm) int nm;
LIST_OF_ITEMS
#undef X
std::vector<int*> derp = {
#define X(nm) &nm,
LIST_OF_ITEMS
#undef X
};
};

Related

X-macro driven C++ template class instantiation

I am trying to instantiate a templatized class based on an X-macro. However, this is giving me syntax errors error: wrong number of template arguments (0, should be 1). What is the correct way to instantiate a templatized class from an x-macro?
#include <string.h>
#include <iostream>
#define FOO \
X(, aaa) \
X(int, bbb) \
template <class T> class A
{
public:
A(){ std::cout << "Hello From A\n";}
};
class B
{
public:
B() {std::cout << "Hello From B\n";}
};
int main()
{
#define X(a,b) \
if (0 == strlen(#a)) { \
printf("%s is empty\n", #b); \
B b; \
} else { \
printf("%s is NOT empty\n", #b); \
A<a> b; \
}
FOO
#undef X
return 0;
}
The issue here isn't that your syntax is wrong, but rather that both branches of the if and else get compiled regardless of whether a is empty or not. The compiler error will trigger because the else branch will try instantiating A<>, which isn't legal.
To fix this, you could consider adding a level of indirection. Here's a modified piece of code where the type AHelper serves to output something of the proper type.
/* By default, use A. */
template <typename... Args> struct AHelper {
using result = A<Args...>;
};
/* But not if there are no arguments. */
template <> struct AHelper<> {
using result = B;
};
int main() {
#define X(a,b) \
AHelper<a>::result b;
FOO
#undef X
}
(Initially, I thought this would be as easy as using if constexpr rather than if, but in a non-template context the compiler is supposed to evaluate both the if and else branch and the same compiler error results.)

How to access enum class through index?

enum class Fruit { apple, orange, pear };
enum class Color { red, green, orange };
template <typename T> struct Traits;
//I have to return the appropriate value(string) of color and fruit in their respective structs.
//I could do this by switch case method but I specifically wanted to know, how do I access an enum class through index
template<>
struct Traits<Fruit>{
static string name(int i){
if(i>-1&&i<3){
return Fruit::static_cast<Fruit>(i);
}
else{
return "unknown";
}
}
};
template<>
struct Traits<Color>{
static string name(int i){
if(i>-1&&i<3){
return Color::static_cast<Color>(i);
}
else{
return "unknown";
}
}
};
I want to return the appropriate string present in the respective structs at their respective indices.
The static_cast is not working and compiler is giving an error that it can't cast. I wonder if it is possible to access enum class through index at all.
Error:
could not convert ‘(Fruit)i’ from ‘Fruit’ to
‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’
return static_cast<Fruit>(i);
As error says you cannot directly convert enum class into string. But you can convert int into enum class using static_cast
After converting int into enum you can go ahead with enum to string conversion. There are many ways to do and this so answer has good summary of that
static string name(int i) {
if (i>-1 && i<3) {
Fruit f = static_cast<Fruit>(i);
return name(f);
}
else {
// return default value or throw error
return "";
}
}
static string name(Fruit f) {
//convert f and return string
}
You could have an array of fruit names in parallel to your enum to have names:
std::string FruitNames[] = { "apple", "orange", "pear" };
Now you can use this directly to get names from. If you are willing to invest a little into x-macros, you can generate both your array and the enum from with one single macro:
#define FRUITS GEN_FRUIT(apple) GEN_FRUIT(orange) GEN_FRUIT(pear)
#define GEN_FRUIT(X) X,
enum class Fruit { FRUITS };
#undef GEN_FRUIT
#define GEN_FRUIT(X) [Fruit::X] = #X,
std::string const FruitNames[] = { FRUITS };
#undef GEN_FRUIT
Admitted, harder to read, on the other hand, you have just one single location to maintain both enum and array...
You could even support explicit values:
#define FRUITS GEN_FRUIT_V(apple, 1) GEN_FRUIT(orange) GEN_FRUIT_V(pear, 4)
#define GEN_FRUIT(X) X,
#define GEN_FRUIT_V(X, Y) X = (Y),
enum class Fruit { FRUITS };
#undef GEN_FRUIT
#undef GEN_FRUIT_V
#define GEN_FRUIT(X) GEN_FRUIT_V(X, Fruit::X)
#define GEN_FRUIT_V(X, Y) std::make_pair(static_cast<Fruit>(Y), std::string(#X)),
std::unordered_map<Fruit, std::string> const FruitNames({ FRUITS });
// or (tree) map, if you prefer
//std::map<Fruit, std::string> const FruitNames({ FRUITS });
#undef GEN_FRUIT
#undef GEN_FRUIT_V
With upcoming C++20 (or a compiler supporting designated initialisers as extension), you could use an array again:
#define GEN_FRUIT(X) X,
#define GEN_FRUIT_V(X, Y) X = (Y),
enum class Fruit { FRUITS };
#undef GEN_FRUIT
#undef GEN_FRUIT_V
#define GEN_FRUIT(X) [static_cast<int>(Fruit::X)] = #X,
#define GEN_FRUIT_V(X, Y) GEN_FRUIT(X)
std::string const FruitNames[] = { FRUITS };
#undef GEN_FRUIT
#undef GEN_FRUIT_V
Yet future music... Result in above example would be an enum with gaps and the array filled with empty strings at the gaps. If gaps are large (or negative enum values exist), this approach would result in large array as well, so rather not appropriate any more, you might prefer falling back to the map solution again...

How can I make this more robust, given that there is no reflection in C++?

I have the following class:
template <typename T>
T get_value(std::string name, T defaultValue)
{
// read values from config file and assign to var; if not found use defaultValue
return defaultValue;
}
class A {
public:
A()
: myvar1_(get_value("myvar1", 0))
, myvar2_(get_value("myvar2", 1.5))
{
}
int myvar1_;
double myvar2_;
std::string asString() const {
std::stringstream str;
str << "myvar1 = " << myvar1_
<< ", myvar2 = " << myvar2_;
return str.str();
}
private:
// other things exist here, unrelated to myvar1/2
};
Where get_value is a function that reads the values of the variables from some config file, and if not found uses the default value. I also have myvar1_ and myvar2_ as public member variables, because they get accessed directly, and I'd like to keep that feature and do not want to add a potential function hop.
Now, you can see that I have typed myvar1 or myvar1_ in quite a few different places, and I'd like to make this more robust, so that I can type, somewhere somehow, myvar1_, "myvar1", 0 once (as opposed to having typed myvar1_ 3 times and "myvar1" twice), and automagically get the above functions called and values filled. I have a lot of variables and they get added or removed fairly frequently, and sometimes I forget to initialize them, or mistype the string name in set_value, or forget to add a new variable to asString.
Is that possible to do? I'd appreciate any hints.
Option 1: A DSL that generates your code from definitions.
Option 2: Use a class facade over the config lookup, backed presumably by access to your config class, and combine with C++11's member initialization. This will reduce the number of times you have to repeat a variable, which can then be hidden with a little macroing if you're up for that.
#include <iostream>
template<typename T>
class DV {
T value_;
public:
DV(const char* name, const T& defaultValue)
{
// read values from config file and assign to var; if not found use defaultValue
value_ = defaultValue;
}
operator const T& () const { return value_; }
};
using DVInt = DV<int>;
using DVStr = DV<const char*>;
struct A {
int i_ = DVInt("i", 42);
const char* str_ = DVStr("str", "hello");
A() = default;
};
int main() {
A a;
std::cout << a.i_ << ", " << a.str_ << "\n";
}
Demo: http://ideone.com/RAyKwI
-- Edit --
Reducing to one instance with a macro.
Instead of the using statements:
#include <iostream>
template<typename T>
class DV {
T value_;
public:
DV(const char* name, const T& defaultValue)
{
// read values from config file and assign to var; if not found use defaultValue
value_ = defaultValue;
}
operator const T& () const { return value_; }
};
#define CONCAT(x, y) x ## y
#define DVDecl(T, name, def) T CONCAT(name, _) = DV<T>(#name, def)
#define DVInt(name, def) DVDecl(int, name, def)
#define DVCStr(name, def) DVDecl(const char*, name, def)
struct A {
DVInt(myvar1, 42);
DVCStr(myvar2, "hello");
A() = default;
};
int main() {
A a;
std::cout << a.myvar1_ << ", " << a.myvar2_ << "\n";
}
http://ideone.com/JmgfH9
This doesn't eliminate your need to manually add them to asString, though.
I ended up with a macro solution, heavily borrowing from a different SO answer:
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__)))
#define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__)))
#define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__)))
#define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__)))
#define EVAL(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
#define MAP_END(...)
#define MAP_OUT
#define MAP_GET_END0() 0, MAP_END
#define MAP_GET_END1(...) 0
#define GET_MACRO(_0, _1, _2, _3, _4, NAME, ...) NAME
#define MAP_GET_END(...) GET_MACRO(_0, ##__VA_ARGS__, MAP_GET_END1, MAP_GET_END1, MAP_GET_END1, MAP_GET_END1, MAP_GET_END0)(__VA_ARGS__)
#define MAP_NEXT0(item, next, ...) next MAP_OUT
#define MAP_NEXT1(item, next) MAP_NEXT0 (item, next, 0)
#define MAP_NEXT(item, next) MAP_NEXT1 (MAP_GET_END item, next)
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
#define DEFINE_VARS_T(TYPE, NAME, DEFAULT_VALUE) \
TYPE NAME##_;
#define DEFINE_VARS(TUPLE) DEFINE_VARS_T TUPLE
#define CONSTRUCT_VARS_T(TYPE, NAME, DEFAULT_VALUE) \
NAME##_ = get_value(#NAME, DEFAULT_VALUE);
#define CONSTRUCT_VARS(TUPLE) CONSTRUCT_VARS_T TUPLE
#define PRINT_VARS_T(TYPE, NAME, DEFAULT_VALUE) \
<< #NAME << " = " << NAME##_ << ", "
#define PRINT_VARS(TUPLE) PRINT_VARS_T TUPLE
#define CONFIG_VARS(...) \
MAP(DEFINE_VARS, __VA_ARGS__) \
A() { \
MAP(CONSTRUCT_VARS, __VA_ARGS__) \
} \
std::string asString() const { \
std::stringstream str; \
str MAP(PRINT_VARS, __VA_ARGS__); \
return str.str(); \
}
template <typename T>
T get_value(std::string name, T defaultValue)
{
// read values from config file and assign to var
return defaultValue;
}
class A {
public:
CONFIG_VARS
(
(int, myvar1, 0),
(double, myvar2, 1.5),
(std::string, myvar3, "what")
)
private:
// other things exist here, unrelated to myvar1/2
};

define an enum using include

I define an enums using include,since there are different enums which have the same enumeration data and I want to reuse it:
#define X(SYM) SYM
#define X_INIT(SYM,VAL) SYM = VAL
/// Destination for scalar memory read instruction
enum SSRC
{
#include "GenericInstructionFields1.h"
#include "ScalarInstructionFields.h"
#include "GenericInstructionFields2.h"
};
enum SDST
{
#include "GenericInstructionFields1.h"
};
#undef X_INIT
#undef X
};
But I can`t compile the code for SDST. The compiler writes redefinition for a fields of SSRC,which comes from "GenericInstructionFields1.h". What is the cause of the problem and how can it be solved?
//GenericInstructionFields1.h
/// SGPR0 to SGPR103: Scalar general-purpose registers.
X_INIT(ScalarGPRMin,0),
X(ScalarGPR),
X_INIT(ScalarGPRMax,103),
/// 104 – 105 reserved.
X(Reserved104),
X(Reserved105),
X_INIT(Reserved,2),
/// vcc[31:0].
X_INIT(VccLo, 106),
/// vcc[63:32].
X(VccHi),
You can't have enumerations with the same enumerators in the same namespace. This would reproduce your problem:
enum X {A,B};
enum Y {A};
either use namespaces or prefix your enum values.
Enums are not like namespaces.
You will see the same error with the following
enum A
{
P, Q
};
enum B
{
P, Q
};
You can achieve what you want by this
struct A
{
enum { P, Q };
};
struct B
{
enum { P, Q };
};
You can now use A::P, A::Q, B::P & B::Q
Or in your case
#define X(SYM) SYM
#define X_INIT(SYM,VAL) SYM = VAL
/// Destination for scalar memory read instruction
struct SSRC
{
enum
{
#include "GenericInstructionFields1.h"
#include "ScalarInstructionFields.h"
#include "GenericInstructionFields2.h"
}
};
struct SDST
{
enum
{
#include "GenericInstructionFields1.h"
}
};
#undef X_INIT
#undef X
};
You can now use SSRC::ScalarGPRMax and SDST::ScalarGPRMax

Method to return pointer, pointing to array object declared in another header,

I'm getting a little stuck with two entwined problems.
First, I want to have an array of pointers to objects on the heap. (objects that are declared in another header)
Second, then I want to have a method return a pointer to one of those objects.
My current code is the result of a bit of fumbling, and will fail because I can't use "bar" as a return type without fully declaring it. But I can't see how else to solve the problem. I tried to make "getBar" a pointer to a function, but then I don't know how to make it access **barArray without it being a member method.
Any help would be much appreciated :D
My code:
foo.h
#ifndef FOO_H
#define FOO_H
//forward declaration
class bar;
class foo
{
public:
//constructor
foo(int x);
//method
bar * getBar(int y);
private:
int howManyBars;
bar **barArray;
};
#endif
foo.cpp
#include "foo.h"
#include "bar.h"
//constructor
foo::foo(int x)
{
howManyBars = x;
barArray = new bar *[howManyBars];
for (int i=0; i < howManyBars ; i++)
{
barArray[i] = NULL; //set all pointers to NULL
}
}
//method
bar * foo::getBar(int y)
{
y = (y - 1);
// if the pointer is null, make an object and return that
if (barArray[y] == NULL)
{
barArray[y] = new bar();
}
return barArray[y];
}
bar.h
#ifndef BAR_H
#define BAR_H
#include <iostream>
class bar
{
public:
void test(){std::cout << "I'm alive!\n";};
};
#endif
Other than a few typos, this compiles fine:
You need a semi-colon after defining the bar class.
bar * foo:getBar(int y)
should be:
bar * foo::getBar(int y)
3.
bar[i] = NULL; //set all pointers to NULL
should be:
barArray[i] = NULL; //set all pointers to NULL