How to initialize nested array of structs in C++? - c++

I have the following definitions:
struct Display_font_char
{
unsigned char * data;
int originX;
int originY;
unsigned int width;
unsigned int height;
unsigned int delta;
};
struct Display_font
{
Display_font_char * chars;
unsigned char rangeStart;
unsigned char rangeEnd;
};
How can I initialize it inplace? I'm trying:
const Display_font font =
{
{
{
{ 1, 2, 3 },
1,
2,
3u,
4u,
5u
}
},
1u,
2u
}
However, I'm getting an error: "Cannot use value of type int to initialize field of type Display_font_char *"

You cannot initialise a pointer with a braced init list of multiple values.
Here is an example of how you could initialise an instance of the class:
unsigned char uc[] = { 1, 2, 3 };
Display_font_char fc {
uc,
1,
2,
3u,
4u,
5u,
};
const Display_font font =
{
&fc,
1u,
2u,
};
As a sidenote, if you were to switch to C, then you could use compound literals:
const struct Display_font font =
{
&(struct Display_font_char){
(unsigned char []){ 1, 2, 3 },
1,
2,
3u,
4u,
5u
},
1u,
2u
};
But alas, compound literals don't exist in C++.

In order to initialize your pointers, you have to create the pointed structures first, for example using new.
Notice that, if you want to treat Display_font.chars and Display_font_char.data as arrays, you should save their sizes (e.g. through a size_t data_size member).
In the example below I create the arrays with new[] and I delete them later on with delete[].
[Demo]
#include <iostream> // cout
#include <ostream>
struct Display_font_char
{
unsigned char* data;
size_t data_size;
int originX;
int originY;
unsigned int width;
unsigned int height;
unsigned int delta;
};
std::ostream& operator<<(std::ostream& os, const Display_font_char& f)
{
os << "\tdata = [";
for (size_t i{0}; i < f.data_size; ++i)
{
os << ((i == 0) ? "" : ", ") << static_cast<int>(f.data[i]);
}
os << "]\n";
os << "\toriginX = " << f.originX << "\n";
os << "\toriginY = " << f.originY << "\n";
os << "\twidth = " << f.width << "\n";
os << "\theight = " << f.height << "\n";
os << "\tdelta = " << f.delta << "\n";
return os;
}
struct Display_font
{
Display_font_char* chars;
size_t chars_size;
unsigned char rangeStart;
unsigned char rangeEnd;
};
std::ostream& operator<<(std::ostream& os, const Display_font& f)
{
os << "chars = [\n";
for (size_t i{0}; i < f.chars_size; ++i)
{
os << ((i == 0) ? "" : ", ") << f.chars[i];
}
os << "]\n";
os << "rangeStart = " << static_cast<int>(f.rangeStart) << "\n";
os << "rangeEnd = " << static_cast<int>(f.rangeEnd) << "\n";
return os;
}
int main()
{
const Display_font font = {
new Display_font_char[1]{
new unsigned char[4]{1, 2, 3},
3, // data size
1, 2,
3u, 4u, 5u
},
1, // chars size
1, 2
};
std::cout << font;
delete[] font.chars[0].data;
delete[] font.chars;
}
This other version uses std::vector instead of Display_font_char* and unsigned char*, so you don't have to do care about memory allocations/deallocations.
[Demo]
#include <iostream> // cout
#include <ostream>
#include <vector>
struct Display_font_char
{
std::vector<unsigned char> data;
int originX;
int originY;
unsigned int width;
unsigned int height;
unsigned int delta;
};
std::ostream& operator<<(std::ostream& os, const Display_font_char& f)
{
os << "\tdata = [";
bool first{true};
for (auto& d : f.data)
{
os << (first ? "" : ", ") << static_cast<int>(d);
first = false;
}
os << "]\n";
os << "\toriginX = " << f.originX << "\n";
os << "\toriginY = " << f.originY << "\n";
os << "\twidth = " << f.width << "\n";
os << "\theight = " << f.height << "\n";
os << "\tdelta = " << f.delta << "\n";
return os;
}
struct Display_font
{
std::vector<Display_font_char> chars;
unsigned char rangeStart;
unsigned char rangeEnd;
};
std::ostream& operator<<(std::ostream& os, const Display_font& f)
{
os << "chars = [\n";
bool first{true};
for (auto& c : f.chars)
{
os << (first ? "" : ", ") << c;
first = false;
}
os << "]\n";
os << "trangeStart = " << static_cast<int>(f.rangeStart) << "\n";
os << "rangeEnd = " << static_cast<int>(f.rangeEnd) << "\n";
return os;
}
int main()
{
const Display_font font{
{ { {1, 2, 3}, 1, 2, 3u, 4u, 5u } },
1,
2
};
std::cout << font;
}

Related

Joining two integers into one bigger integer in C++

I need to have two separate 16-bit integers, that can form a 32-bit integer together. But I need them to be updated whenever I change any of them. Let's say I change the value of the 32-bit, I need it to be automatically written over the two 16-bit ones and vice versa. Is this possible?
You can use a proxy class to represent your 32-bit integer:
class Proxy {
private:
uint16_t &_high, &_low;
public:
Proxy(uint16_t &high, uint16_t &low) : _high(high), _low(low) {}
Proxy &operator=(uint32_t whole) {
_high = whole >> 16;
_low = whole & 0xffff;
return *this;
}
operator uint32_t() const {
return (_high << 16) | _low;
}
};
int main() {
uint16_t high = 0xa, low = 0xb;
Proxy whole(high, low);
std::cout << std::hex;
std::cout << whole << '\n'; // a000b
high = 0xc;
low = 0xd;
std::cout << whole << '\n'; // c000d
whole = 0xe000f;
std::cout << high << ' ' << low << '\n'; // e f
return 0;
}
By providing operator uint32_t, Proxy can be implicitly converted to uint32_t in most cases.
This gets really easy with c++20 which has bit_cast. It can even be used in constexpr functions. Here are freestanding encapsulated, and really simple direct (no extra functions or classes) versions:
#include <iostream>
#include <array>
#include <cstdint>
#include <bit>
using std::uint32_t, std::uint16_t;
// Free standing functions
void place_low16(std::array<uint16_t, 2>& arr, uint16_t x) {arr[0] = x;}
void place_high16(std::array<uint16_t, 2>& arr, uint16_t x) {arr[1] = x;}
void place_32int(std::array<uint16_t, 2>& arr, uint32_t i){arr = std::bit_cast<std::array<uint16_t, 2>>(i);}
uint32_t get_ui32(const std::array<uint16_t, 2>& arr) {return std::bit_cast<uint32_t>(arr);}
// encapsulated
struct PackedInt16 {
std::array<uint16_t, 2> two_uint32s;
void place_low16(uint16_t x) {two_uint32s[0] = x;}
void place_high16(uint16_t x) { two_uint32s[1] = x; }
void place_32int(uint32_t i) { two_uint32s = std::bit_cast<std::array<uint16_t, 2>>(i); }
uint32_t get_ui32() { return std::bit_cast<uint32_t>(two_uint32s); }
};
int main()
{
// free standing functions
std::array<uint16_t, 2> packed_ints;
place_low16(packed_ints, static_cast<uint16_t>(0xffff'ffff)); //store in low int16
place_high16(packed_ints, static_cast<uint16_t>(0x1)); // store in high int16
uint32_t x = get_ui32(packed_ints); // get 32 bit uint
place_32int(packed_ints, x); // store 32 bit uint in packed int16s
std::cout << x << " " << packed_ints[0] << " " << packed_ints[1] << '\n';
// ouput: 131071 65535 1
// encapsulated
PackedInt16 packed_ints2;
packed_ints2.place_low16(static_cast<uint16_t>(0xffff'ffff));
packed_ints2.place_high16(static_cast<uint16_t>(0x1));
uint32_t x2 = packed_ints2.get_ui32();
packed_ints2.place_32int(x2);
std::cout << x2 << " " << packed_ints2.two_uint32s[0] << " " << packed_ints2.two_uint32s[1] << '\n';
// ouput: 131071 65535 1
// and now the direct approach: No functions, no classes
std::array<uint16_t, 2> packed_ints3;
packed_ints3[0] = static_cast<uint16_t>(0xffff'ffff);
packed_ints3[1] = 1;
uint32_t x3 = std::bit_cast<uint32_t>(packed_ints3);
packed_ints3 = std::bit_cast<std::array<uint16_t, 2>>(x3);
std::cout << x3 << " " << packed_ints3[0] << " " << packed_ints3[1] << '\n';
// ouput: 131071 65535 1
}
You could define a class that behaves similar to a uint16_t which works with a uint32_t value stored as reference.
In some cases there's a difference though, e.g. a conversion to uint16_t won't happen automatically in some cases.
class Uint32BitView16
{
uint32_t& m_data;
unsigned m_shift;
public:
constexpr Uint32BitView16(uint32_t& data, unsigned shift)
: m_data(data),
m_shift(shift)
{
}
constexpr operator uint16_t() const
{
return (m_data >> m_shift);
}
constexpr Uint32BitView16& operator=(uint16_t value)
{
m_data = (m_data & ~static_cast<uint32_t>(0xffff << m_shift)) | (value << m_shift);
return *this;
}
};
int main() {
uint32_t data = 0x01020304;
Uint32BitView16 v1(data, 0);
Uint32BitView16 v2(data, 16);
std::cout << std::hex;
std::cout << static_cast<uint16_t>(v1) << '\n'; // 304
std::cout << static_cast<uint16_t>(v2) << '\n'; // 102
data = 0xffff0000;
std::cout << static_cast<uint16_t>(v1) << '\n'; // 0
std::cout << static_cast<uint16_t>(v2) << '\n'; // ffff
v1 = 0xff;
std::cout << data << '\n'; // ffff00ff
}

How to parse a const char* from a double / long without the std::string library?

How can I parse a const char* from a double or long?
Mainly because my code is a lot faster when I use a const char*, so i decided to create a small base string class. But my code to parse a double has some bugs.
My code only works partially. Some help would be very appreciated.
I am using macos, g++ & c++17.
Code:
#include <iostream>
class bstring {
public:
const char* characters;
bstring(const char* c = "") { characters = c; }
static bstring parse(const double number, int precision = 100) {
// Convert.
int decimal, sign;
char *buffer;
buffer = ecvt(number, precision, &decimal, &sign);
int n = strlen(buffer);
// Add decimal.
char before[decimal];
strncpy(before, 0 + buffer, decimal);
char after[n - decimal - 1];
strncpy(after, decimal + buffer, n - decimal - 1);
// Remove zero padding.
int removed = 0;
while (true) {
size_t n = sizeof(after) - removed;
size_t index_to_remove = n - 1;
if (after[index_to_remove] == '0') {
for (size_t i = index_to_remove; i < n - 1; ++i) {
after[i] = after[i + 1];
}
removed += 1;
} else { break; }
}
bool is_zero = removed == sizeof(after);
int after_size = sizeof(after)-removed;
char* nafter = (char*)malloc(sizeof(char) * after_size);
// Concat.
char* new__{ new char[strlen(before) + 1 + after_size] };
new__ = strcpy(new__, before);
new__ = strcat(new__, ".");
if (is_zero) {
char a[] = "0";
new__ = strcat(new__, a);
} else {
new__ = strcat(new__, after);
}
// Assign.
bstring s = new__;
delete[] new__; new__ = NULL;
return s;
//
}
};
std::ostream& operator <<(std::ostream &s, bstring x) { return s << x.characters; }
int main() {
std::cout << "Should be " << "-1234.39950" << ": " << bstring::parse(-1234.39950) << std::endl;
std::cout << "Should be " << "-1.0" << ": " << bstring::parse(-1.0) << std::endl;
std::cout << "Should be " <<"0.0" << ": " << bstring::parse(0.0) << std::endl;
std::cout << "Should be " <<"0.3897495" << ": " << bstring::parse(0.3897495) << std::endl;
std::cout << "Should be " <<"1.0" << ": " << bstring::parse(1.0) << std::endl;
std::cout << "Should be " <<"100.00" << ": " << bstring::parse(1000.0) << std::endl;
std::cout << "Should be " <<"10000.000" << ": " << bstring::parse(1000000.0) << std::endl;
std::cout << "Should be " <<"1000000.0000" << ": " << bstring::parse(1000000000.0) << std::endl;
std::cout << "Should be " <<"1000000000.0000" << ": " << bstring::parse(1000000000000.0) << std::endl;
std::cout << "Should be " <<"1000000000000.0000" << ": " << bstring::parse(1000000000000000.0) << std::endl;
}
Edit:
Is this piece of code okay? Or am I doing something wrong by not deleting it / By where I assign the new__ to.
// Concat.
bstring concat(const char* c) {
int n = ::strlen(characters) + ::strlen(c);
if (n == 0) { return bstring(); }
if (::strlen(c) == 0) { return bstring(characters); }
char* new__{ new char[n + 1] };
new__ = strcpy(new__, characters);
new__ = strcat(new__, c);
// const char* n = new__;
// delete[] new__; new__ = NULL;
bstring s = new__;
return s;
}

Why member function address are so far away from free functions?

Taking this example: https://godbolt.org/z/gHqCSA
#include<iostream>
template<typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return(*p)(Args...) ) {
return os << (void*)p;
}
template <typename ClassType, typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return (ClassType::*p)(Args...) )
{
unsigned char* internal_representation = reinterpret_cast<unsigned char*>(&p);
os << "0x" << std::hex;
for(int i = 0; i < sizeof p; i++) {
os << (int)internal_representation[i];
}
return os;
}
struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main() {
std::cout << "0. " << &test_debugger::var << std::endl;
std::cout << "1. " << fun_void_void << std::endl;
std::cout << "2. " << fun_void_double << std::endl;
std::cout << "3. " << fun_double_double << std::endl;
}
// Prints:
// 0. 0x7018400100000000000
// 1. 0x100401080
// 2. 0x100401087
// 3. 0x100401093
I see the address of the member function is 0x7018400100000000000, which is understandable because member functions pointers have 16 bytes while free function as 0x100401080 have only 8 bytes.
However, why the member function address 0x7018400100000000000 is so far away from free function address 0x100401080? i.e., |0x7018400100000000000 - 0x100401080| = 0x70184000FFEFFBFEF80?
Why it is not closer i.e., something like 0x100401... instead of 0x701840...? Or I am printing the member function address wrong?
Your architecture is little-endian. The low byte of the address is in the first byte of p, so your address is being printed out backwards.
Fixed code which automatically detects little/big endian: https://godbolt.org/z/XSvT5R
#include <iostream>
#include <iomanip>
#include <sstream>
inline bool is_big_endian() {
long int longvalue = 1;
// https://stackoverflow.com/questions/8978935/detecting-endianness
unsigned char* representation = reinterpret_cast<unsigned char*>(&longvalue);
return ( (unsigned) representation[sizeof(long int) - 1] ) == 1;
}
template<typename Pointer>
std::ostream& print_pointer(std::ostream& os, const Pointer& pointer) {
const unsigned char* representation = (unsigned char*) &pointer;
int precision = 0;
bool haszeros = false;
unsigned firsthexdigit;
unsigned secondhexdigit;
std::ostringstream stream;
stream.flags( os.flags() );
stream << std::hex;
#define print_pointer_HEX_DIGIT \
firsthexdigit = (unsigned) representation[index] >> 4 & 0xf; \
secondhexdigit = (unsigned) representation[index] & 0xf; \
if( haszeros || firsthexdigit ) { \
precision++; \
haszeros = true ; \
stream << firsthexdigit; \
} \
if( haszeros || secondhexdigit ) { \
precision++; \
haszeros = true ; \
stream << secondhexdigit; \
}
if( is_big_endian() ) {
for(int index = 0; index < static_cast<int>(sizeof pointer); index++) {
print_pointer_HEX_DIGIT
}
}
else {
for(int index = static_cast<int>(sizeof pointer - 1); index >= 0 ; index--) {
print_pointer_HEX_DIGIT
}
}
if( os.precision() - ++precision > 0 ) {
return os << "0x" + std::string( os.precision() - ++precision, '0' ) + stream.str();
}
return os << "0x" + stream.str();
}
template<typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return(*pointer)(Args...) ) {
return print_pointer(os , pointer);
}
template <typename ClassType, typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return (ClassType::*pointer)(Args...) ) {
return print_pointer(os , pointer);
}
struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main() {
std::cout << "0. " << &test_debugger::var << std::endl;
std::cout << "1. " << fun_void_void << std::endl;
std::cout << "2. " << fun_void_double << std::endl;
std::cout << "3. " << fun_double_double << std::endl;
std::cout << "4. " << std::setfill('0') << std::setw(16) << fun_void_void << std::endl;
std::cout << "5. " << std::setprecision(16) << fun_void_double << std::endl;
}
// Prints:
// 0. 0x100402e80
// 1. 0x100401118
// 2. 0x10040111f
// 3. 0x10040112b
// 4. 000000x100401118
// 5. 0x0000010040111f

need help on operator << overloading functions

The error I'm getting right now is:
multiple definition of operator<<(std::ostream&, SingleSequence& s)
the error location is at one of the overload operator function:
std::ostream& operator<<(std::ostream& os, const SingleSequence& s)
{
os << s.name << " " << s.seq << " " << s.length << " " << s.gccontent << " " << s.type;
return os;
}
This is the driver part:
#include"Sequences.h"
#include <iostream>
#include <fstream>
#include <cmath>
#include <ctime>
#include <cstdlib>
using namespace std;
int main(){
cout << "Assignment #1" << endl;
Sequences mysequences;
cout << mysequences;
cout << "Sorted by name" << endl;
mysequences.sortByName();
cout << mysequences;
cout << "Sorted by length" << endl;
mysequences.sortByLength();
cout << mysequences;
cout << "... done!" << endl;
}
This is the Sequences.h
#ifndef SEQUENCES_H_
#define SEQUENCES_H_
#include<string.h>
#include<strings.h>
#include<string>
#include<iostream>
using namespace std;
enum sequenceType { dna, rna, protein };
struct SingleSequence{
std::string name;
std::string seq;
int length;
double gccontent;
sequenceType type;
};
class Sequences {
public:
Sequences();
virtual ~Sequences();
int getListSize() const{return datasize;}
const SingleSequence& get( int i) const{
if (i>=0 && i < datasize)
return data[i];
throw OUT_OF_BOUNDS;;//{ if (i>=0 && i < datasize)
}
// return data[i];
// throw OUT_OF_BOUNDS;} // C++ has exceptions - you can even throw ints;
void sortByName();
void sortByLength();
friend std::ostream& operator<<(std::ostream& os, const SingleSequence& s) ;
friend std::ostream& operator<<(std::ostream& os, const Sequences& seqs) ;
int datasize;
private:
/*
* Remember to keep all data members private
*/
static const int MAX_LIST_SIZE = 20;
SingleSequence data[MAX_LIST_SIZE];
static const int OUT_OF_BOUNDS = -1;
};
std::ostream& operator<<(std::ostream& os, const SingleSequence& s)
{ os << s.name << " " << s.seq << " "
<< s.length << " " << s.gccontent << " "<<s.type;
return os;
}
#endif /* SEQUENCES_H_ */
-------------------------------------------------------------
This is the main cpp file
#include "Sequences.h"
#include <iostream>
using namespace std;
Sequences::Sequences() {
data[0] = { "KCNH2 Primer Pair 1 Forward", "CCAACTGGTGGACCGTCATT", 20, 55.0, dna };
data[1] = { "KCNH2 Primer Pair 1 Reverse", "GACAGCCAGGTGAACATCCA", 20, 55.0, dna };
data[2] = { "KCNH2 Primer Pair 2 Forward", "TGGATGTTCACCTGGCTGTC", 20, 55.0, dna };
data[3] = { "KCNH2 Primer Pair 2 Reverse", "CCACGGAACCTCTGGCAATA", 20, 55.0, dna };
data[4] = { "KCNH2 Primer Pair 3 Forward", "GAACGGAAGTGTGCCAACTG", 20, 55.0, dna };
data[5] = { "KCNH2 Primer Pair 3 Reverse", "ACAGCCAGGTGAACATCCAG", 20, 55.0, dna };
data[6] = { "KCNH2 Primer Pair 4 Forward", "CTGGATGTTCACCTGGCTGT", 20, 55.0, dna };
data[7] = { "KCNH2 Primer Pair 4 Reverse", "ATTTCCACGGAACCTCTGGC", 20, 55.0, dna };
data[8] = { "KCNH2 Primer Pair 5 Forward", "TGAAAACCGCTCGTCTGC", 18, 55.6, dna };
data[9] = { "KCNH2 Primer Pair 5 Reverse", "GGTGGAGCATGTGTTGTT", 18, 50.0, dna };
datasize = 10;
}
void Sequences::sortByName(){
for(int i = 0; i < 10; i++){
//int flag = 1;
SingleSequence temp;
for(int j = 0; j < 9; j++){
if (data[j].name.compare(data[j+1].name) > 0){
temp = data[j+1];
data[j+1] = data[j];
data[j] = temp;
}
}
}
}
void Sequences::sortByLength(){
for(int a = 0; a < 10; a++){
SingleSequence temp1;
for(int b = 0; b < 9; b++){
if (data[b].length > data[b+1].length){
temp1 = data[b+1];
data[b+1] = data[b];
data[b] = temp1;
}
}
}
}
std::ostream& operator<<(std::ostream& os, const Sequences& seqs)
{os << " Sequences object " << endl;
for (int i=0; i < seqs.getListSize(); i++ )
os << " " << (i+1) <<": " << seqs.get( i ) << endl;
return os;
}
You have two definition of the same operator << function in .h and .cpp. Hence, multi-definition error.
Keep the declaration in .h. Makes sure it is outside of the class
std::ostream& operator<<(std::ostream& os, const SingleSequence& s);
std::ostream& operator<<(std::ostream& os, const Sequences& seqs);
And write the definition in you .cpp file
std::ostream& operator<<(std::ostream& os, const Sequences& seqs)
{
os << " Sequences object " << endl;
for (int i = 0; i < seqs.getListSize(); i++)
os << " " << (i + 1) << ": " << seqs.get(i) << endl;
return os;
}
std::ostream& operator<<(std::ostream& os, const SingleSequence& s)
{
os << s.name << " " << s.seq << " "
<< s.length << " " << s.gccontent << " " << s.type;
return os;
}

Vector of any type from given struct

Is there any way to create a vector of any type of given structures?
struct STUDENCI
{
int indeks;
string imie;
string nazwisko;
};
struct PRZEDMIOTY
{
int id;
string nazwa;
int semestr;
};
struct SALE
{
string nazwa;
int rozmiar;
bool projektor;
double powierzchnia;
};
vector<ANY TYPE FROM STUDENCI, PRZEDMIOTY, SALE> TAB[3];
You can use a the variant library from boost (www.boost.org):
std::vector<boost::variant<STUDENCI, PRZEDMIOTY, SALE> > v;
E.g. Live on Coliru
#include <boost/variant.hpp>
#include <iostream>
#include <string>
#include <vector>
using std::string;
struct STUDENCI
{
int indeks;
string imie;
string nazwisko;
friend std::ostream& operator << (std::ostream& os, STUDENCI const& v) {
return os << "STUDENCI { " << v.indeks << ", " << v.imie << ", " << v.nazwisko << " }";
}
};
struct PRZEDMIOTY
{
int id;
string nazwa;
int semestr;
friend std::ostream& operator << (std::ostream& os, PRZEDMIOTY const& v) {
return os << "PRZEDMIOTY { " << v.id << ", " << v.nazwa << ", " << v.semestr << " }";
}
};
struct SALE
{
string nazwa;
int rozmiar;
bool projektor;
double powierzchnia;
friend std::ostream& operator << (std::ostream& os, SALE const& v) {
return os << "SALE { " << v.nazwa << ", " << v.rozmiar << ", "
<< std::boolalpha << v.projektor << ", " << v.powierzchnia << " }";
}
};
typedef std::vector<boost::variant<STUDENCI, PRZEDMIOTY, SALE> > Vector;
int main()
{
Vector v;
v.push_back(STUDENCI { 1, "imie", "nazwisko" });
v.push_back(PRZEDMIOTY { 1, "eng101", 3 });
v.push_back(SALE { "auditorium", 42, true, 250 });
for (auto& element: v)
std::cout << element << "\n";
}
Prints
STUDENCI { 1, imie, nazwisko }
PRZEDMIOTY { 1, eng101, 3 }
SALE { auditorium, 42, true, 250 }
That's what unions are for, see the reference for more information on the topic:
Union declaration