How can I simplify this Code?
mfer::i_value* make_empty_value(mfer::tag tag_)
{
if (tag_ == mfer::tag::mwf_ble) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_ble>());
} else if (tag_ == mfer::tag::mwf_chn) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_chn>());
} else if (tag_ == mfer::tag::mwf_blk) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_blk>());
} else if (tag_ == mfer::tag::mwf_seq) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_seq>());
} else if (tag_ == mfer::tag::mwf_man) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_man>());
} else if (tag_ == mfer::tag::mwf_ivl) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_ivl>());
} else if (tag_ == mfer::tag::mwf_sen) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_sen>());
} else if (tag_ == mfer::tag::mwf_wfm) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_wfm>());
} else if (tag_ == mfer::tag::mwf_pre) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pre>());
} else if (tag_ == mfer::tag::mwf_off) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_off>());
} else if (tag_ == mfer::tag::mwf_nul) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_nul>());
} else if (tag_ == mfer::tag::mwf_pnt) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pnt>());
} else if (tag_ == mfer::tag::mwf_nte) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_nte>());
} else if (tag_ == mfer::tag::mwf_txc) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_txc>());
} else if (tag_ == mfer::tag::mwf_flt) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_flt>());
} else if (tag_ == mfer::tag::mwf_skw) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_skw>());
} else if (tag_ == mfer::tag::mwf_mss) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_mss>());
} else if (tag_ == mfer::tag::mwf_pnm) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pnm>());
} else if (tag_ == mfer::tag::mwf_pid) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pid>());
}
return nullptr;
}
Briefly stating,
mfer::tag is enumeration, defined like enum tag {}; in namespace mfer.
mfer::i_value is abstract class.
class i_value {};
mfer::t_value is templated class like,
template <mfer::tag tag_type>
class t_value : public i_value {};
At this moment, I don't know how to simplify this make_empty_value().
Ideally, I want to make it like this:
mfer::i_value* make_empty_value(mfer::tag tag_)
{
return memory_manager::instance().add(new mfer::t_value<tag_>());
}
But I know that it is template, so above one doesn't make sense.
Is there any idea simplify this code? (Some modern C++ features, Boost libraries, and so on)
With a little template work, we can get the factory function down to:
i_value* make_empty_value(tag tag_type)
{
static constexpr auto factory = make_factory(all_tags());
auto index = std::size_t(tag_type - tag::first);
if (index < tag::ntags) {
return memory_manager::instance().add(factory[index]());
}
else {
return nullptr;
}
}
Full code below.
The i_value generator map is built at compile time, allowing constant-time lookup.
constraints:
the values in the enum must be consecutive, but they need not begin at zero.
this demo requires c++14. It can be easily adapted to work with c++11. For c++03 we'd want to reach out to boost mpl or boost_pp.
complete working example:
#include <array>
#include <utility>
#include <deque>
#include <iostream>
// minimal implementation of virtual base
class i_value {
public:
virtual void prove() const = 0;
virtual ~i_value() = default;
};
// tag enum - note that we have supplied some extra introspection information
// these could just as well be constexpr integers outside the enum
enum tag
{
ble,
chn,
blk,
seq,
first = ble, // first available tag
last = seq, // last available tag
ntags = last-first // number of tags
};
/// Function to offset an index sequence by the distance from
/// zero to the first available tag - in case the first tag is not zero
template<std::size_t...tags>
constexpr auto tag_offset(std::index_sequence<tags...>)
{
return std::index_sequence<(tags + tag::first)...>();
}
/// Function to compute an index sequence of all valid tags
constexpr auto all_tags()
{
return tag_offset(std::make_index_sequence<std::size_t(ntags)>());
}
/// Factory function to generate a derived class for a given tag
template <tag tag_type>
class t_value : public i_value {
void prove() const override { void(std::cout << "I have tag " << tag_type << std::endl); }
~t_value() { void(std::cout << "tag " << tag_type << " destroyed" << std::endl); }
};
template<tag tag_type>
i_value* make_instance()
{
return new t_value<tag_type>();
}
/// Function to generate a 'factory' - an array of factory functions, one for
/// each tag in the variadic template argument tags...
/// Note that the array is zero-based, the tags may not be. All we care about
/// here is the size of the list of tags (and their values)
///
template<std::size_t...tags>
constexpr auto make_factory(std::index_sequence<tags...>)
{
return std::array<i_value* (*)(), sizeof...(tags)>
{
&make_instance<static_cast<tag>(tags)>...
};
}
// minimal memory manager
struct memory_manager {
struct impl {
i_value* add(i_value* item) {
_ivalues.push_back(item);
return item;
};
~impl() {
for (auto i = _ivalues.rbegin() ; i != _ivalues.rend() ; ++i) {
delete *i;
}
}
std::deque<i_value*> _ivalues;
};
static impl& instance()
{
static impl _instance = {};
return _instance;
}
};
// here is resulting factory function.
i_value* make_empty_value(tag tag_type)
{
static constexpr auto factory = make_factory(all_tags());
auto index = std::size_t(tag_type - tag::first);
if (index < tag::ntags) {
return memory_manager::instance().add(factory[index]());
}
else {
return nullptr;
}
}
// test
int main()
{
for(auto tag_type : { tag::ble, tag::chn })
{
auto pvalue = make_empty_value(tag_type);
pvalue->prove();
}
}
expected output:
I have tag 0
I have tag 1
tag 1 destroyed
tag 0 destroyed
You can map the tags to a factory method;
typedef std::unordered_map<mfer::tag,std::function<mfer::i_value*()>> TagMap;
TagMap create_tag_map()
{
TagMap map;
map[mfer::tag::mwf_ble] = [](){ return new mfer::t_value<mfer::tag::mwf_ble>(); };
map[mfer::tag::mwf_chn] = [](){ return new mfer::t_value<mfer::tag::mwf_chn>(); };
map[mfer::tag::mwf_blk] = [](){ return new mfer::t_value<mfer::tag::mwf_blk>(); };
//...
return map;
}
The create_empty_value method could then look like this:
mfer::i_value* make_empty_value(mfer::tag tag_)
{
static TagMap factory = create_tag_map();
auto it = factory.find( tag_ );
if( it != factory.end() )
{
return memory_manager::instance().add( it->second() );
}
return nullptr;
}
see simplified version Live on Coliru
You can create a recursive template function if the enumerate value follows a known pattern (by default next enumerate value equals previous enumerate +1):
//anonymous namespace to "help innliner"
namespace{
//This function return the next enumerates value:
constexpr mref::tag next_tag(mref::tag tag_) {
return static_cast<mref::tag>(
static_cast<std::underlying_type_t<mref::tag>>(tag_) + 1);
}
//The compute function is wrapped in a structure to enable template
//specialization:
template <mref::tag Tag> struct add_to_mem_manager {
static mfer::i_value* compute(mref::tag tag_) {
if (Tag == tag_) {
return memory_manager::instance().add(
new mfer::t_value<Tag>());
} else {
return add_to_mem_manager<next_tag(Tag)>::compute(tag_);
}
}
};
//Specialization for last enumerate
template <> struct add_to_mem_manager<mfer::tag::mwf_pid> {
static mref::ivalue* compute(mref::tag tag_) {
assert(mref::tag::mwf_pid == tag_);
return memory_manager::instance().add(
new mfer::t_value<mfer::tag::mwf_pid>());
}
};
}
mfer::i_value* make_empty_value(mfer::tag tag_){
//call with template parameter equals to the
//the enumerate whose values is the smallest
return add_to_mem_manager<mfer::tag::mwf_ble>::compute(tag_);
}
If you don't know the constitutive rule of your enumerate, you cannot do this,( generaly constitutive law is as in this example, x[i+1]=x[i]+1, or x[i+1]=x[i]<<1 (left shift).) Otherwise their is no way to iterate over elements of an enumeration.
Note: The function compute will certainly be inlined, but in doubt you can use
compiler specific attribute as __forceinline with MSVC or __attribute__((__always_inline__)) with GCC or clang.
Not directly using your example, but you can do something on the below lines, i.e converting enum to a type.
enum Type {
Type_A,
Type_B,
};
template <Type T>
struct Enum2Type {
constexpr static const Type type = T;
};
template <typename T>
mfer::i_value* make_empty_value(T tag_type)
{
return memory_manager::instance().add(new mfer::t_value<tag_type.type>());
}
auto val = make_empty_value(Enum2Type<Type_A>());
auto val2 = make_empty_value(Enum2Type<Type_B>());
The only scope of simplification I see is in removing the boilerplate code by replacing with a fixed macro. This will be soothing to the viewer.
Instead of so many if-else if, make it a switch/case as below:
#define CASE(TAG) \
case TAG: return memory_manager::instance().add(new mfer::t_value<TAG>())
mfer::i_value* make_empty_value(const mfer::tag tag_)
{
switch(tag_) {
{
CASE(mfer::tag::mwf_ble);
CASE(mfer::tag::mwf_chn);
CASE(mfer::tag::mwf_blk);
//...
default: break;
}
return nullptr;
}
#undef CASE
Related
The context of this question revolves around hard typed genetic programming.
I would like to return a function pointer from a function but these functions pointers point to functions with different return types. In another stack overflow question (Function Pointers with Different Return Types C) a return type of union was suggested however I am struggling with the implementation.
I am fairly new to C++ so please forgive my ignorance if it shows.
#include <iostream>
#include <string>
float Add(float a, float b) { return a + b; }
bool IfElse(bool a) { if (a) { return true; } else { return false; }; }
union return_type
{
float(*ffptr)(float, float);
bool(*bfptr)(bool);
};
union fptr(std::string OPERATION) {
if (OPERATION == "Add") {
return_type.ffptr = Add;
} else if (OPERATION == "IfElse") {
return_type.bfptr = IfElse;
}
return return_type;
}
int main() {
std::cout << fptr("Add") << std::endl
return 0;
}
I expect (or rather would like) this to print the address of the function Add
TL;DR version: I think you may be trying to hammer a solution into fitting a problem. Consider using something like the Visitor pattern to decouple the problem so that you don't need to know the type of the data.
A union isn't a type. It's a type of types, like a class or a struct. In order to use a return_type, you have to make an object that is a return_type. That means
union fptr(std::string OPERATION) {
if (OPERATION == "Add") {
return_type.ffptr = Add;
} else if (OPERATION == "IfElse") {
return_type.bfptr = IfElse;
}
return return_type;
}
needs to look more like
return_type fptr(std::string OPERATION) {
return_type result; // make a return_type object
if (OPERATION == "Add") {
result.ffptr = Add; // set a member of the object
} else if (OPERATION == "IfElse") {
result.bfptr = IfElse;
}
return result; // return the object
}
Then you can
int main() {
std::cout << fptr("Add").ffptr(10,20) << std::endl; // call the stored function
return 0;
}
The big problem with unions is knowing what is in them. You can only safely use ffptr if ffptr was the last member set.
int main() {
std::cout << fptr("Add").bfptr(true) << std::endl;
return 0;
}
will compile, but will not behave well at all when run. What will happen is undefined, but odds are good that it won't be pretty.
You have to be absolutely certain that the function stored in the union is the correct one. If your compiler is up to date, you can use std::variant to help out here. It will at least tell you you're headed in the wrong direction by throwing an exception
#include <iostream>
#include <string>
#include <variant>
float Add(float a, float b) { return a + b; }
bool IfElse(bool a) { if (a) { return true; } else { return false; }; }
using return_type = std::variant<float (*)(float a, float b), bool (*)(bool a)>;
return_type fptr(std::string OPERATION) {
return_type result;
if (OPERATION == "Add") {
result = Add;
} else if (OPERATION == "IfElse") {
result = IfElse;
}
return result;
}
int main() {
std::cout << std::get<float (*)(float a, float b)>(fptr("Add"))(10,20) << std::endl;
try
{
std::cout << std::get<bool (*)(bool a)>(fptr("Add"))(true) << std::endl;
}
catch (const std::bad_variant_access& e)
{
std::cout << e.what() << std::endl;
}
return 0;
}
But at the end of the day it's still not all that useful. I think you may find the Visitor pattern or one of its friends more helpful.
You were close.
Be careful not to conflate the declaration of the (union) type name and the function return value. Since you want to reference pointer address, I added a void* to your union (fpaddr), so you can clearly identify that you are printing an address. Note that your fptr("Add") returned the union, and you needed to disambiguate which interpretation of the union you wanted.
#include <iostream>
#include <string>
float Add(float a, float b) { return a + b; }
bool IfElse(bool a) { if (a) { return true; } else { return false; }; }
//typedef //in C you would use 'typedef'
union fp_return_t
{
float(* ffptr )(float, float);
bool(* bfptr )(bool);
void* fpaddr;
}; //fp_return_t; //in C you would give the name here
fp_return_t fptr(std::string OPERATION) {
fp_return_t fp_return;
if (OPERATION == "Add") {
std::cout << "Add:" << (void*) Add << std::endl;
fp_return.ffptr = Add;
} else if (OPERATION == "IfElse") {
std::cout << "IfElse:" << (void*) IfElse << std::endl;
fp_return.bfptr = IfElse;
}
return fp_return;
}
int main() {
std::cout << fptr("Add").fpaddr << std::endl;
return 0;
}
I'm not quite sure what the ultimate goal is but here's how you can make the above compile and print the function address (printing of a plain Add added for comparison):
#include <iostream>
#include <string>
float Add(float a, float b) { return a + b; }
bool IfElse(bool a) { return a; }
union return_type
{
float(*ffptr)(float, float);
bool(*bfptr)(bool);
};
union return_type fptr(std::string OPERATION) {
union return_type r;
if (OPERATION == "Add") {
r.ffptr = Add;
} else if (OPERATION == "IfElse") {
r.bfptr = IfElse;
}
return r;
}
int main()
{
/*the void*-cast is technically nonportable but it's hard to
print fn-pointers portably*/
std::cout << reinterpret_cast<void*>(Add) << '\n';
/*you should know which union member is active: */
std::cout << reinterpret_cast<void*>(fptr("Add").ffptr) << '\n';
/*^should be the same address*/
return 0;
}
i have a big problem.. I wonna select the Storage Service via a wrapper class. The returning value must be an object within the storage service class. I pasted my current approach. But my mindset didn't worked so far.
Error:
error: inconsistent deduction for auto return type: ‘SQL*’ and then ‘REDIS*’ return new REDIS();
The big wish is to have an interface class which defines the struct and some "driver classes" which contains all necessary operations for the target storage service.
I hope you have another approach, how I can solve this problem..
#include <iostream>
class StorageTemplate {
public:
virtual bool UserhasSurName() = 0;
virtual bool UserhasGivenName() = 0;
};
class SQL: public StorageTemplate {
public:
bool UserhasSurName() {
//A SQL QUERY
return true;
}
bool UserhasGivenName() {
//AN ANOTHER SQL QUERY
return true;
}
};
class REDIS: public StorageTemplate {
public:
bool UserhasSurName() {
//A REDIS CALL
return false;
}
bool UserhasGivenName() {
//A REDIS CALL
return false;
}
};
class controller {
public:
auto test(int select) {
if( select == 1)
{
return new SQL();
} else {
return new REDIS();
}
}
};
int main(int argc, char const *argv[])
{
controller cont;
auto schnitzel = cont.test(1);
auto mitzel = cont.test(2);
std::cout << schnitzel->UserhasSurName() << std::endl;
std::cout << mitzel->UserhasSurName() << std::endl;
}
The problem you are facing is the following: Consider your function
auto test(int select) {
if (select == 1) {
return new SQL();
} else {
return new REDIS();
}
}
If you trying to evaluate test(1) this expands to
auto test(int select) {
if (true) {
return new SQL();
} else {
return new REDIS();
}
}
which results in a type error!
I show you three workarounds for your problem:
1. Function template and if constexpr
Make test a function template and check for the correct type using the C++17 feature if constexpr:
template<typename T>
auto test() {
if constexpr(std::is_same<T, SQL>::value) {
return new SQL();
} else {
return new REDIS();
}
}
Use it in main() like this:
int main(){
controller cont;
auto schnitzel = cont.test<SQL>();
auto mitzel = cont.test<REDIS>();
std::cout << schnitzel->UserhasSurName() << std::endl;
std::cout << mitzel->UserhasSurName() << std::endl;
}
2. Function template and std::unique_ptr
If you want to avoid using the if constexpr you can simply return an instance of std::unique_ptr instead of a raw pointer. This is the preferred way to do:
template<typename T>
auto test() {
return std::unique_ptr<T>(new T);
}
Alternatively you can just return std::make_unique<T>().
3. Returning an instance of the base class
This is is most obvious solution to avoid the type error: Just return an instance of the base class. As above a solution using smart pointers is preferred here:
std::unique_ptr<StorageTemplate> test(const int select) {
if (select == 1) {
return std::make_unique<SQL>();
} else {
return std::make_unique<REDIS>();
}
}
If you really want to avoid using smart pointers just use raw ones like this:
StorageTemplate* test(const int select) {
if (select == 1) {
return new SQL();
} else {
return new REDIS();
}
}
in this code
auto test(int select) {
if( select == 1)
{
return new SQL();
} else {
return new REDIS();
}
auto can't be deduced because it only match to exact type. so even if SQL and REDIS inherite from StorageTemplate, StorageTemplate won't be deduced. you need to spécifie the type
StorageTemplate* test(int select) {
if( select == 1)
{
return new SQL();
} else {
return new REDIS();
}
Error return Auto in test(),it's return two different types. Change by StorageTemplate*
class controller {
public:
StorageTemplate* test(int select) {
if( select == 1)
{
return new SQL();
} else {
return new REDIS();
}
}
};
Is it possible to use a lambda function to create an alias to a template class function? Something like this:
#include <iostream>
using namespace std;
int calcDouble(int a) { return a * 2; }
int calcMultiply_10(int a) { return a * 10; }
struct foo
{
template<void (*func)(int)>
int generic(int value)
{
return func(value);
}
static auto double_10 = [this] { generic<calcDouble>(10); };
static auto double_20 = [this] { generic<calcDouble>(20); };
static auto multiply_10_20 = [this] { generic<calcMultiply_10>(20); };
}
int main() {
foo f;
cout << "double_10: " <<f.double_10() << endl;
cout << "double_20: " <<f.double_20() << endl;
return 0;
}
Your particular example doesn't compile, and would be dangerous if it did - capturing this by value means that as soon as the class is copied/moved the captured this will point to a wrong or invalid memory location.
Just use member functions:
auto double_10() { return generic<calcDouble>(10); }
auto double_20() { return generic<calcDouble>(20); }
auto multiply_10_20() { return generic<calcMultiply_10>(20); }
live example on wandbox
I'm looking for an efficient way to check if a POD variable is altered between two cycles. I've come up with this solution:
class Foo {
public:
template<typename T>
bool isChanged(T& entry);
void endCycle();
private:
std::map<void*,size_t> entryMap; // <Address orig.,Size>
std::map<void*,void*>oldVals; // <Address orig., Address cpy.>
};
template<typename T> bool Foo::isChanged(T& entry)
{
entryMap[&entry] = sizeof(T);
if(oldVals[&entry] == NULL)
return false;
if(memcmp(&entry, oldVals[&entry], entryMap[&entry]))
return true;
else
return false;
}
void Foo::endCycle()
{
// Copy all the bytes to save them for the next cycle
for( std::map<void*,size_t>::iterator entryIt = entryMap.begin();
entryIt != entryMap.end();
++entryIt)
{
if(oldVals[entryIt->first] == NULL)
oldVals[entryIt->first] = malloc(entryIt->second);
memcpy(oldVals[entryIt->first], entryIt->first, entryIt->second);
}
}
Now i can use it like this:
Foo gBar;
void aFunction()
{
int ar;
char ba[3][3];
// Some code where ar and ba are filled
if(gBar.isChanged(ar))
// Do Something
if(gBar.isChanged(ba))
// Do Something
gBar.endCycle();
}
Is this an efficient way? My goal was a method which is very easy to use inside various cyclically called functions. I cleaned all the init and free logic from the code. Any suggestions? I especially don't like the oldshool malloc, memcpy and memcmp stuff but i don't know any other way how to do it.
Edit: Found a good solution based on Red Alerts suggestions.
I think you can use templates a little more effectively here.
template <typename T>
class Foo
{
public:
static std::map<T*, T> values;
static bool isChanged(T& entry)
{
auto it = values.find(&entry);
if(it == values.end())
{
values[&entry] = entry;
}
else if(entry != it->second)
{
it->second = entry;
return true;
}
return false;
}
};
template <typename T>
std::map<T*, T> Foo<T>::values;
int main() {
int ar = 3;
cout << Foo<int>::isChanged(ar) << endl; // 0
ar = 4;
cout << Foo<int>::isChanged(ar) << endl; // 1
for(auto& value : Foo<int>::values)
cout << value.second << endl; // 4
return 0;
}
This way you get one map per type, and you don't have to worry about inadvertently messing up an alias. You do need to define operator != and have a working copy constructor for your types, but that is much better than blindly using memcmp and memcpy.
You can also make further template specializations for arrays if you need to compare those (will be a bit more code, but nothing very complicated)
Edit: To get you started, this is what your template signature should look like:
template<class T, size_t N> bool isChanged(T(&entry)[N]); //will be called for stack allocated arrays
Or you can use char* to alias all of your values. This will let you use a single map for everything (like you were doing before, but this has no memcpy/memcmp). It will only work for POD. We could manually call the destructor when overwriting the buffer, but since there is no good way to do this in the class's destructor, it's probably best to leave out heap allocated data altogether.
class Foo
{
std::map<char**, char*> values;
public:
~Foo()
{
for(auto& value : values)
{
delete[] value.second;
}
}
template<typename T> bool isChanged(T& entry)
{
char** addr = reinterpret_cast<char**>(&entry);
auto it = values.find(addr);
if(it == values.end())
{
alignas(T) char* oldBuf = new char[sizeof(T)];
T* oldEntry = new(oldBuf) T;
*oldEntry = entry;
values[addr] = oldBuf;
}
else if(entry != *(reinterpret_cast<T*>(it->second)))
{
T* oldEntry = new(it->second) T;
*oldEntry = entry;
return true;
}
return false;
}
};
After many hours i think i found a good solution. The call stays easy and there are no casts. It's a lot more complex than the C-Style version with memcopy but I think its nicer and has also the benefit that it works with complex data not just POD.
class Manager
{
public:
~Manager()
{
funcPtrs.clear();
}
void adFnc(void(*function)())
{
funcPtrs.push_back(function);
}
void runAll()
{
for(auto& val : funcPtrs)
val();
}
private:
std::vector<void (*)()> funcPtrs;
};
Manager gAllClearManager;
template<typename T>
class Data
{
public:
Data()
{
gAllClearManager.adFnc(clearValues);
}
static void clearValues()
{
values.clear();
}
static std::map<T*,std::vector<T>>& getValues() { return values; }
private:
static std::map<T*,std::vector<T>> values;
};
template <typename T>
static bool isChanged(T& entry)
{
const static Data<T>* dataP = new Data<T>();
static std::map<T*,std::vector<T>>& values = dataP->getValues();
auto it = values.find(&entry);
if(it == values.end())
{
values[&entry].push_back(entry);
}
else if(entry != it->second[0])
{
it->second[0] = entry;
return true;
}
return false;
}
template<typename T, size_t N>
bool isChanged(T (&entry)[N])
{
const static Data<T>* dataP = new Data<T>();
static std::map<T*,std::vector<T>>& values = dataP->getValues();
auto it = values.find(entry);
if( it == values.end())
{
for(int i = 0; i < N ; ++i )
values[entry].push_back(entry[i]);
return false;
}
else
{
for(int i = 0; i < N ; ++i )
{
if(it->second[i] != entry[i])
{
for(int j = 0; j < N ; ++j )
{
it->second[j] = entry[j];
}
return true;
}
}
}
return false;
}
template<typename T>
std::map<T*, std::vector<T>> Data<T>::values;
Now i can use it like:
int main() {
int ar;
std::string ba[6];
if(isChange(ar))
// Do something
if(isChange(ba))
// Do something
}
My first template is finally working! :) Thanks again Red Alert.
I'm about to write a parser to read a text file line by line into structs of different types and giving these structs to a callback (observer or visitor - not sure yet).
The text file contains MT-940 data - a SWIFT bank statement.
These lines consist of a marker which specifies the type and some fields - e.g. a date - which should be parsed into type-safe members of my message. Some of these fields are optional - so my question is: How do I represent optional values in D.
C++ provides my things like boost::optional which you might know.
I currenty work around this by implementing an Optional(T) on my own (see code at the end of this post). It is a struct which contains a ValueHolder instance which might be null - which marks the case where no value has been assigned. I overwrote the copy-c'tor and the assignment operator to create a deep-copy of the ValueHolder if necessary.
Is this the way to go? Is there any other - more simple - option I just cannot see?
This is my code - not necessarily feature complete yet:
struct Optional(T)
{
class ValueHolder
{
T value;
this(T v)
{
value = v;
}
}
private ValueHolder m_value;
/* Construction without value / with value */
this(T value)
{
m_value = new ValueHolder(value);
}
/* Copy construction / assignment */
ref Optional!(T) opAssign(Optional!(T) rhs)
out
{
if (rhs.m_value !is null)
{
assert(rhs.m_value != m_value);
}
else
{
assert(m_value is null);
}
}
body
{
m_value = null;
if (rhs)
{
m_value = new ValueHolder(rhs.m_value.value);
}
return this;
}
ref Optional!(T) opAssign(T value)
out
{
assert(hasValue());
assert(m_value.value == value);
}
body
{
if (m_value is null)
{
m_value = new ValueHolder(value);
}
else
{
m_value.value = value;
}
return this;
}
this(Optional!(T) rhs)
out
{
if (rhs.m_value !is null)
{
assert(rhs.m_value != m_value);
}
else
{
assert(m_value is null);
}
}
body
{
if (rhs.m_value !is null)
{
m_value = new ValueHolder(rhs.m_value.value);
}
}
/* Implicit cast to bool */
bool hasValue() const
{
return m_value !is null;
}
X opCast(X: bool)()
{
return hasValue();
}
/* Value access */
T opUnary(string s)() const
in
{
assert(s == "*");
assert(m_value !is null);
}
body
{
return m_value.value;
}
}
/* Default Constructed Struct does not have a value assigned */
unittest
{
Optional!(int) x;
assert(x.hasValue() == false);
assert(!x);
}
/* Construction with value */
unittest
{
Optional!(int) x = 3;
assert(x);
assert(x.hasValue());
}
/* Assignment operator does copy the value */
unittest
{
Optional!(int) x = 3;
Optional!(int) y;
assert(x);
assert(!y);
y = x;
assert(&x != &y);
assert(x);
assert(y);
y = 12;
assert(x.m_value.value != y.m_value.value);
assert(*y == 12);
Optional!(int) z;
x = z;
assert(!x);
assert(!z);
assert(y);
}
For optional values, the D standard library provides the struct template Nullable in the module std.typecons.