How to update unique items in a TArray - c++

I'm adding and updating unique items in a TArray* using the following method.
USTRUCT() struct FMyObject
{
GENERATED_BODY()
UPROPERTY(SaveGame) // example for serialization
FName key;
UPROPERTY(SaveGame) // example for serialization
int someContent;
}
UCLASS() class FMyObjectOwner : public UObject
{
GENERATED_BODY()
AddOrReplaceUnique(const FMyObject& newObject)
{
// Remove possibly existing entry.
const auto index = myObjects.IndexOfByKey(newObject.key);
if (index != INDEX_NONE)
{
myObjects.RemoveAtSwap(index, 1, false);
}
// Add new.
myObjects.Add(newObject);
}
UPROPERTY(SaveGame) // example for serialization
TArray<FMyObject> myObjects;
}
Is there a shorter or more convenient way (removing and adding in one call?) to update unique items in a TArray using UnrealEngine >= 4.24?
*I'm using a TArray instead of a TMap or a TSet since I want it to be garbage collected and to be serialized. I'm not sure if both is supported by the latter container types.

Related

C++ - How to reserve some class objects to only be instantiated by a certain class

I'm defining a unique_id_generator class, which is kind of, sort of a singleton i.e there is just one instance for a given type_id. There can be many different type_ids, but for a specific type_id, there is just one instance.
Now I want to make sure that type_id = 0 goes to a very specific class. Basically just that specific class can use type_id = 0 and then the rest can be used freely.
I'm wondering through which design pattern can I ensure that happens?
I don't want to control or govern type_ids given in general.
FYI, I'm already using a private constructor to block un-guarded instantiation of the class.
I can't control who instantiates a unique_id_generator first. Also based on design, I don't want to route requests for unique ids through the specific class which gets type_id = 0.
Any thoughts/advice is greatly appreciated.
The only solution I could think of is to define 2 instantiation methods. One public and one private as follows:
#define EXCLUSIVE_CNT 1
class UniqueID
{
public:
friend class MySpecialClass;
static UniqueID * GetInstance(int type_id)
{
assert(type_id >= EXCLUSIVE_CNT);
return Instantiate(type_id);
}
private:
int type_id_;
int seq_id_;
std::map<type_id, UniqueID*> type_to_obj_map_;
UniqueID(int type_id)
{
type_id_ = type_id;
seq_id_ = 0;
}
static UniqueID * GetExclusiveInstance(int type_id)
{
assert(type_id < EXCLUSIVE_CNT);
return Instantiate(type_id);
}
static UniqueID * Instantiate(int type_id)
{
if(type_to_obj_map_.find(type_id) == type_to_obj_map_.end())
{
type_to_obj_map_[type_id] = new UniqueID(type_id);
}
return type_to_obj_map_[type_id];
}
};

Object instantiation through factory method not giving desire result

In below code snippet I do require to instantiate the object through factory method in order to call the selected adapter (i.e. adapterTwovalue)but while calling through factory method i am not able to get the desire results. When we assign static declared object's address (i.e adapter = &at) it works but with factory i usually get the blank output.
I tried as well with (adapter = new adapterTwo()) to instantiate the object but output string is giving blank results. As per my requirement i need to populate the all the getters in connect function which is pure virtual function to frame the response.Anybody can suggest how to achieve this using factory method.
#include <iostream>
using namespace std;
class IAdapter
{
public:
enum FactoryList { AdapterOnevalue = 0, AdapterTwovalue };
virtual void connect() = 0;
static IAdapter* CreateList(FactoryList);
virtual ~IAdapter() {}
};
class LibraryOne
{
string property;
public:
void SetConnection(string property)
{
this->property = property;
}
string getConnection()const
{
return property;
}
};
//LibraryTwo
class LibraryTwo
{
string broker;
public:
void SetBroker(string broker1)
{
this->broker = broker1;
}
string getBroker() const
{
return broker;
}
};
//adapterOne
class AdapterOne : public IAdapter
{
LibraryOne one;
string constring;
public:
void SetClientconnection(string constring)
{
one.SetConnection(constring);
}
string GetClientconnection()
{
return one.getConnection();
}
void connect()
{
constring = GetClientconnection();
}
};
//Adapter to use library two
class AdapterTwo : public IAdapter
{
LibraryTwo two;
string brokerstring;
public:
void SetClientbroker(string constring)
{
two.SetBroker(constring);
}
string GetClientbroker()
{
return two.getBroker();
}
void connect()
{
string constring = GetClientbroker();
cout << "final value=" << constring;
}
};
IAdapter* IAdapter::CreateList(FactoryList SelectList)
{
IAdapter *ListObject;
switch (SelectList)
{
case AdapterOnevalue:
ListObject = new AdapterOne();
break;
case AdapterTwovalue:
ListObject = new AdapterTwo();
break;
default:
ListObject = NULL;
}
return ListObject;
}
int main()
{
IAdapter *adapter = 0;
//LibraryTwo obj;
AdapterTwo at;
at.SetClientbroker("amqp");
//cout << at.GetClientbroker();
//adapter = &at; it works
adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);//it doesn't work
//Just do the operation now
adapter->connect();
return 0;
}
You can see the complete solution in below share link.
http://coliru.stacked-crooked.com/a/d8b9d32a1fa989c9
Here is the explanation.
(1) setClientBroker() or all other adapters related setter functionality needs to be implement as a virtual function in Interface with default parameter value " " (blank string).
(2) you need to always use override keyword (c++11) feature in derive class for setters so that compiler will cross check during compilation whether proper virtual method is being overridden or not.
(3) instead of using local raw pointer , always use smart pointer . below is the
implementation link for the same.
http://coliru.stacked-crooked.com/a/2feea991ee90d4a2
With your code I expect the output: final value=.
It will not print final value=amqp cause you need to call SetClientbroker("amqp") on the right adapter object (adapter in your example).
Anyway, I would think about putting a virtual method SetString in the base class, so you could simply do:
int main()
{
IAdapter *adapter = 0;
//LibraryTwo obj;
//AdapterTwo at;
//at.SetClientbroker("amqp");
//cout << at.GetClientbroker();
//adapter = &at; it works
adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);//it doesn't work
//Just do the operation now
adapter->SetString("amqp");//<---------
adapter->connect();
return 0;
}
EDIT after the comment:
You need to cast the object, at this point (as suggested by #Aconcagua).
But IMHO it's not elegant at all. I think you are going to loose the benefits gained with the factory method.
IAdapter* adapter = nullptr;
AdapterTwo at;
adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);
You have created two independent objects here (as calling new within createList): at and the one adapter points to.
AdapterTwo at;
at.SetClientbroker("amqp");
Now sure you get the expected output if you let adapter point to at, but how could the other object be aware of the string you set in the first one?
adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);
adapter->SetClientbroker("amqp"); // (*) !!!
You need to set the broker at the other object, too. As being different objects, you even can set the brokers independently:
AdapterTwo at;
at.SetClientbroker("amqp");
IAdapter* adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);
adapter->SetClientbroker("aconcagua"); // (*) !!!
Output now would be (if you called connect on both objects):
final value=amqp
final value=aconcagua
Only: The marked lines ((*)) won't compile as your base class does not provide the appropriate setter!
There are now different solutions for this problem. You could, for instance, just cast the object:
// if you are REALLY 100% sure the object is of appropriate type:
static_cast<AdapterTwo*>(adapter)->setClientBroker("...");
// if NOT:
AdapterTwo* a2 = dynamic_cast<AdapterTwo*>(adapter);
if(a2)
a2->setClientBroker("...");
else
// appropriate error handling
You could find a more generic name for the set/get Broker/ClientConnection functions, have them already pure virtual within IAdapter and override them in the two implementing adapter classes, so you could then just call adapter->setXYZ("ampq");. [Edit: according to your comment to the question, not an option in the given case]
My personal favourite is providing an additional parameter to your createList function such that the setter would already be called within the factory - possibly with appropriate default: empty string, if you opt for a std::string parameter, or nullptr in case of char const*. You'd only call the setter if the parameter is not matching the default, of course... Alternatively, you could have two overloads.

Proper placement of "toCSV" function: In each object, or in a generic class?

There is a set of classA objects, containing several data fields, and inside each classA object, there is a set of classB objects containing additional data fields.
At some point I want to generate a CSV file.
My initial approach is to implement a .toCSV() in both classA and classB and do the following in main.cpp:
string completecsv;
foreach(classA ca, setOfClassA)
completecsv.append(ca.toCSV());
And inside classA.toCSV()
string csv;
csv.append(field1);
csv.append(field2);
csv.append(...);
foreach(classB cb, setOfClassB)
csv.append(cb.toCSV());
return csv;
And finally in classB.toCSV()
string csv;
csv.append(field1);
csv.append(field2);
csv.append(...);
return csv;
Now, my other approach was to create a class named something like OutputManager, that is in charge of everything regarding the CSV generation, keeping the MVC pattern more clearly separated.
Any thoughts regarding this two approaches?
Many thanks.
If there are lots of properties of classA and classB that make sense to include in a report, regardless of the report format (CSV, XML, Json, etc.), then it sounds like classA and classB are actually data classes without much logic.
If that is the case, I'd keep the report generation separate from them to easily make it possible to extend the reporting mechanism with other output formats if needed.
To cater for a hierarchical output format (like XML or Json), it might make sense to let types that you read properties from to also expose a 'children' property, so that it can be looped through and applied recursively.
For each type that gets output, it could expose a name-value collection of its 'outputtable' data that the OutputManager then chooses what to do with.
Something like this, where the OutputManager would get the 'root' IOutputtable (classB in this case) and just loop over its name-values and then do the same with its children, recursively.
interface IOutputtable
{
NameValueCollection Items { get; }
IEnumerable<IOutputtable> Children { get; }
}
class A : IOutputtable
{
private int _baz;
public NameValueCollection Items {
get {
return new NameValueCollection() {
{ "baz", _baz.ToString() }
};
}
}
public IEnumerable<IOutputtable> Children {
get {
return Enumerable.Empty<IOutputtable>();
}
}
}
class B : IOutputtable
{
private int _foo;
private string _bar;
private List<A> _as = new List<A>();
public NameValueCollection Items {
get {
return new NameValueCollection() {
{ "foo", _foo.ToString() },
{ "bar", _bar }
};
}
}
IEnumerable<IOutputtable> Children {
get { return _as; }
}
}

Pre-constructor initialization

My problem is like this, I have a class named "Product" and another class named "Agriculture", the "Agriculture" class is inheriting the "Product" class.
When I summon the "Agriculture" constructor obviously the "Product" constructor is summoned first.
The question is, can I initialize one of the product's members via a set method first?
If you have:
class Product { ... };
class Agriculture : public Product { ...};
you can't escape the standard rule that the base object is constructed before the derived object. You have no chance to intervene in this order, nor set anything in Product before it's constructor starts.
Recommendation:
The best design for your need would be to foresee a Product constructor that takes as additional parameter(s) the value(s) that you want to set:
class Product {
string origin;
public:
Product () : origin("tbd") { }
Product (string withorigin) { ...}
void setOrigin (string myorigin) { origin=myorigin; }
};
class Agriculture : public Product {
public:
Agriculture () : Product ("Earth") { ...}
};
Workaround:
If such design would not fit your needs, the only thing you could imagine, would be to have a static member in Product. This member would then be independent of any Product, and could thus be set before an object is constructed.
class Product {
static string defaultCurrency;
string currency;
public:
Product () : currency(defaultCurrency) { ... }
static void setDefaultCurrency (string cur) { defaultCurrency=cur; }
};
class Agriculture : public Product { ... };
int main() {
Product::setDefaultCurrency("EUR");
Agriculture a1;
}
It's more error prone: the construction result depends on order of operations not related to the construction. This could be a problem for example in case of multithreading, if several threads construct objects at same moment.
Product constructor is called firstly, and you set some values inside this constructor. So why you still want to initialize one of the product's members via a set method first?

wxTreeItemId how to get data

I'm tried to create my own version of the wxTreeItemId which stores extra data. Sew below:
TreeItemId.h
#ifndef TREE_CTRL
#define TREE_CTRL
#include "wx/treectrl.h"
#include "Particle System.h"
class TreeItemId : public wxTreeItemId
{
public:
TreeItemId(ParticleSystem* system);
private:
ParticleSystem* particleSystem;
};
TreeItemId.cpp
TreeItemId::TreeItemId(ParticleSystem* system)
: wxTreeItemId()
{
particleSystem = system;
}
I want to use an event to get the selected TreeItem but I can't work out a way of using my treeItem class rather than the standard.
I want to do something in the line of:
void TopRightPanel::OnSelChanged(wxTreeEvent& event)
{
TreeItemId *item = (TreeItemId)event.GetItem();
}
This doesn't work though... Any advice would be appreciated. Do I need to use my own version of wxTreeItemData?
You should subclass your data object from wxTreeItemData instead of wxTreeItemId.
Let's say you have MyItemData : public wxTreeItemData {}; then
wxTreeItemId itemId = event.GetItem();
MyItemData * data = (MyItemData *)m_MyTreeCtrl->GetItemData(itemId);
if(data) { /* Doo what you need here */ }
In order to set the item data you need to use InsertItem() method and specify the data object there. Or use SetItemData() for existing item and pass item ID and data object to this method.