Pushing C++ Objects to Lua - c++

I need help pushing Games Objects created in C++ into our Lua enviroment. this an example of what that would look like we have the following GameObjects being created in C++
class Player
{
public:
Player(std::string pName, int ObjID)
{
this -> name = pName;
this -> objectId = ObjID
}
std::string name;
int objectId;
};
Now our objects are all being created in C++ and we don't want to have constructors for it in our Lua scripts. I'm open to using either sol or LuaBridge, I've researched their examples and they show how to push classes with constructors and pushing functions, but is it possible to push userdata with properties instead of functions? so that in our lua script instead of doing obj:GetName() we can do obj.name
a bit more details of how we're creating objs and hope to push it:
Player* player1 = new Player("Player1", 1)
Player* player2 = new Player("Player2", 2)
Then hopefully we can push these to the environment somehow, thank you for any help!
What i tried with luabridge:
luabridge::getGlobalNamespace(this->lua_state)
.beginClass<GameObject>("GameObject")
.addProperty("Name", &GameObject::Name)
.endClass();
but as I understand this would be if I want to create new classes through Lua, not pushing classes already created. Tried something similar with sol as well.

Alrighty so it seems like I was able to figure it out using sol, after some research on the suggestions from the comments. my solution was to make a small wrapper for my gameobjects:
class Lua_GameObject
{
public:
Lua_GameObject(GameObject* obj) {
this -> real_obj = obj;
objects.push_back(this);
}
std::string GetName() const {
auto name_str = this->real_obj->GetName();
int len = strlen(reinterpret_cast<const char*>(name_str));
return std::string(name_str, name_str + len);
}
static std::vector<Lua_GameObject*> ObjectList() {
return Lua_GameObject::objects;
}
private:
GameObject* real_obj;
static std::vector<Lua_GameObject*> objects;
};
after this I use sol in a different file to expose the class and create the objects like this:
// Exposes Lua_Object to the enviroment
this->lua.new_usertype<Lua_GameObject>(
"GameObject",
// No Constructors from lua
sol::no_constructor,
// Name Property
"Name", sol::property(&Lua_GameObject::GetName),
// List of all objects
"ObjectList", &Lua_GameObject::ObjectList
);
now everytime I create a Lua_GameObject it will automatically be added to the list, just need to add a handle to delete them later:
// Creates GameObject
GameObject* obj = new GameObject("ObjName", 123);
//Creates new lua Object which will automatically add it to GameObject:ObjectList()
Lua_GameObject* lua_obj = new Lua_GameObject(obj);
thank you all for your help!

Related

C++ can't access field from inherited class

Hello guys a have a problem, that i can't access field tablica[i]->help, in generuj function, its saying that this field is not existing in class Task.
How can i achieve it ?
class Task
{
protected:
string contents;
int id_pyt;
int nr_pyt;
};
class Task4Answ : public Task
{
private:
int help;
public:
Task4Answ(string contents1, int id,int nr,int help1)
{
contents=contents1;
id_pyt=id;
nr_pyt=nr;
help=help1;
}
};
class TaskCollection
{
protected:
Task *collection[60];
public:
friend class Generator;
TaskCollection()
{
collection[0] = new Task4Answ("Ile jest por roku w Polsce? \na) 1 \nb) 2 \nc) 3 \nd) 4",1,0);
collection[1] = new Task4Answ("Kto wygral tegoroczny Roland Garros? \na) Federer \nb) Djokovic \nc) Nadal \nd) Thiem",1,1);
class Generator
{
protected:
Task *tablica[10];
TaskCollection T1;
public:
Generator(){}
void Generuj()
{
if(T1.collection[x]->id_pyt==1)
{
tablica[i]=new Task4Answ("0",0,0);
tablica[i]->contents=T1.collection[x]->contents;
tablica[i]->id_pyt=T1.collection[x]->id_pyt;
tablica[i]->nr_pyt=T1.collection[x]->nr_pyt;
tablica[i]->help=T1.collection[x]->help; //here is the problem
}
}
}
Or maybe there is some other solution of the project im doing now.
Thanks for any help.
The problem is in this line:
tablica[i]=new Task4Answ("0",0,0);
Although you have called the Task4Answ constructor, you are also assigning the memory address returned by new to a Task pointer. Effectively, you have casted the Task4Answ pointer to a Task pointer. On the lines that follow, C++ only sees tablica[i] as a reference to a Task pointer. You need to change:
protected:
Task *tablica[10];
TaskCollection T1;
...to this:
protected:
Task4Answ *tablica[10]; // Task was changed to Task4Answ
TaskCollection T1;
That should allow C++ to see tablica as an array of Task4Answ pointers instead of Task pointers.
Edit: it looks like help is also private. You will have to change help to public or add TaskCollection::TaskCollection() as a friend. Otherwise, C++ will not let you get or set help.
Edit: the OP added that tablica[i] might contain instances of other classes that inherit from Task. In that case, you could do something like this:
void Generuj()
{
if(T1.collection[x]->id_pyt==1)
{
Task4Answ* newTask = new Task4Answ("0",0,0);
newTask->contents=T1.collection[x]->contents;
newTask->id_pyt=T1.collection[x]->id_pyt;
newTask->nr_pyt=T1.collection[x]->nr_pyt;
newTask->help=T1.collection[x]->help; // You will still have to change this from being private.
tablica[i] = newTask;
}
}
}
Later on, in order to access help, you will need to implement some sort of way of checking whether tablica[i] is a Task4Answ and not an instance of some other class that inherits from Task, perhaps by implementing a method in Task named IsTask4Answ that returns false in Task but is overridden to return True in Task4Answ. You can then cast the pointer back to Task4Answ with something like the static_cast operator. In other words:
// Add these functions to the class definitions:
virtual bool Task::IsTask4Answ() const {
return false;
}
bool Task4Answ::IsTask4Answ() const override {
return true;
}
// Later, you can do this:
if(tablica[i].IsTask4Answ()){
Task4Answ* t = static_cast<Task4Answ*>(tablica[i]);
t->help; // Again, you'll have to change this from being private.
}
Although I suggest figuring out a different data structure where you do not need to do any casting, this will allow you to access help.
Do note the virtual keyword in the first function above; it allows the function to be dynamically bound, which means that the code will check whether to call Task::IsTask4Answ() or Task4Answ::IsTask4Answ() at runtime instead of at compile time.

Can someone provide me with a simple example of a unique_ptr being set and retrieved

How would i write the following with smart ptrs:
class App
{
public:
Project* GetProject();
SetProject( Project* project );
private:
Project* project;
}
Project* project = new Project();
App* app = new App();
app.SetProject( project );
Project* retrievedProject = app.GetProject();
I am mostly interested in the method paramters and return techniques... Is something like this correct?
class App
{
public:
void App::SetProject( unique_ptr<Project> project )
{
this->project.swap( project );
}
Project* App::GetProject() const
{
return this->project.get();
}
private:
unique_ptr< Project > project;
}
...
unique_ptr< Project > project( new Project() );
unique_ptr< App > app( new App() );
app.SetProject( project );
unique_ptr< Project > retrievedProject = app.GetProject();
This is related to this question: Should i stop using * pointers? - A few entry questions
I am slightly confused at what the rules are now... Thanks in advance!
Edit for minor question:
Galik, you had mentioned that it's rare that you want to declare Project outside of the function call and then pass it into with release... I have the following (for example):
unique_ptr<Project>project( new Project() );
project.Title = "Bob";
project.Type = PROJECTYPE.DISTRIBUTION;
project.Manager = manager;
app.SetProject( project.release() );
Is this perfectly normal? My issue is that I am actually creating tools for gaming and i'm worried that the constant flagging for deletion is going to be causing micro stutters as garbage collection is kicking in... Am I just worrying for worry sake?
The answer to this really depends on what you are wanting to do. You should use smart pointers to express ownership and you should not really be passing smart pointers about if you can at all help it.
If for example your App class is supposed to take care of the various Project objects and only give access to the Project to outsiders then you should express that by returning a reference to the project.
That way several callers can access the internal Project that is owned by the App.
Rather like this:
class Project
{
public:
void do_stuff() {}
};
// This class is the Project Owner
// Meaning it is responsible for
// managing its life-time
class App
{
public:
// return a reference expressing the caller
// does not gain ownership merely access
Project& GetProject() { return *project; }
// replace configured Project
// accepting a std::unique_ptr expresses the fact that
// this function takes ownership of the Project
void SetProject(std::unique_ptr<Project> project) { this->project = std::move(project); }
private:
std::unique_ptr<Project> project;
};
int main()
{
App app; // don't make pointers if we don't have to
// project has ownership of the raw pointer
std::unique_ptr<Project> project(new Project());
// std::move() transfers ownership to the function's parameter
app.SetProject(std::move(project));
// get a reference to the internal Project
Project& retrievedProject = app.GetProject();
// work with configured Project
retrievedProject.do_stuff();
}
Here is a simple example with unique_ptr
#include <iostream>
#include <memory>
struct Project
{
~Project()
{
std::cout << "Destructor..." << std::endl;
}
};
class App
{
std::unique_ptr<Project> _uptr;
public:
// set _uptr to nullptr by default
App(std::unique_ptr<Project> uptr = nullptr): _uptr(std::move(uptr)){}
std::unique_ptr<Project> GetProject()
{
return std::move(_uptr);
};
void SetProject(std::unique_ptr<Project> project)
{
// need std::move, unique ownership
_uptr = std::move(project);
};
};
int main()
{
std::unique_ptr<Project> project(new Project);
std::unique_ptr<App> app(new App);
// need std::move, unique ownership
app->SetProject(std::move(project));
}

Linking pointers to pointers between classes (communicating classes)

I've tried to solve my problem for 2 days now and failed miserably. Internet does not help.
What I'm trying to do is to communicate two classes which reside within another class.
This is my first "big" project so I assume my design is terrible for you guys.
Also, my program is split between a lot of files which may be confusing.
Lets hit it! For the sake readability, I've changed every member to public.
This is my MainOGLController class which is the main class that controls everything my program does:
class MainOGLController
{ // I deleted constructor/destructor from this quote
public:
DisplayController* Display;
StellarManager* Manager; // it will need to use something from Display
void RenderScene();
bool CreateNewDisplay(int, char*[]); // argc argv
}
Ok, this is how i create instance of this class in file with main():
#include "MainOGLController.h"
MainOGLController Controller;
int main(int argc, char* argv[])
{
if ( Controller.CreateNewDisplay(argc, argv) ) return 1; // if it fails then exit;
// some opengl code here
return 0;
}
Now you are probably wondering how does the CreateNewDisplay method look like:
bool MainOGLController::CreateNewDisplay(int argc, char* argv[])
{
Display = new DisplayController(argc, argv);
Manager = new StellarManager(&Display); // me trying to make reference to Display
// to be able to use it within Manager
//ogl code
else return 0;
}
OK, so I'm creating Manager there and now we should see how i created the StellarManager class:
class StellarManager
{
std::vector<Stellar*> VectorManager; // objects from this vector will need to use
// ptrDisplay to access Display
DisplayController* ptrDisplay;
StellarManager(DisplayController&);
void addEntity();
};
Now for the constructor:
StellarManager::StellarManager(DisplayController& _p) // me trying to do anything
{
*ptrDisplay = _p;
}
So at this point I should have instance of MainOGLController, and within it, a pointer to DisplayController and StellarController, where StellarController should have its own pointer to the same DisplayController.
Now somewhere withing working piece of code I'm calling the addEntity method:
void StellarManager::addEntity()
{
VectorManager.push_back(new Stellar(&ptrDisplay); // sending ptrDisplay so that the
// Stellar object can use it
}
Stellar class is defined like this:
class Stellar
{
public:
DisplayController* ptrDisplay;
Stellar(DisplayController**);
void Draw(); // finally, heres where i want to use this Display pointer
};
Stellar constructor:
Stellar::Stellar(DisplayController** _p)
{
*ptrDisplay = **_p;
}
OKAY! Thats the final piece. All i want to do now is simply call method Draw which belongs to Stellar class and use Display which is located in MainOGLController.
Manager->VectorManager[0].Draw();
Oh and the Draw looks just like this:
void Stellar::Draw(int _mode)
{
GLMatrixStack* mvm = &(ptrDisplay->modelViewMatrix);
mvm->Scale(2, 0.5, 0.5); // Scale is a method from GLMatrixStack
}
Thats all folks, if theres any better way of doing this, im all ears.
What I did does not work, I'm able to use the *ptrDisplay from Stellar class but nothing happens so I guess I'm not using its reference but a copy.
Sorry, I know this is a lot of code and it may be very confusing. I just dont know what to do now...
It looks like the problem is here:
Stellar::Stellar(DisplayController** _p)
{
*ptrDisplay = **_p;
}
You're dereferencing a pointer (ptrDisplay) that was never initialized. This results in undefined behavior. I think this captures what you wanted to do:
Stellar::Stellar(DisplayController* _p) : ptrDisplay(_p)
{
}
It's not necessary to pass a pointer-to-pointer-to-DisplayController; all your Stellar class needs is a pointer to a DisplayController. Moreover, it sounds like you don't want to dereference _p and copy it, so simply copying the pointer (via ptrDisplay(_p)) will result in ptrDisplay pointing to the same object as _p.

Qt: Pass variables from ClassA to ClassB via Class C

I have the following layout:
MainWindow <--------- Settings
\
\
V
PerformOps
MainWindow takes in variables passed from Settings, and passes them on to PerformOps.
If the Settings class is not used, MainWindow passed on defaults to PerformOps.
Currently I do:
class Settings{
public:
Settings(*parent);
var1, var2, var3
.
.
void exec();
}
class PerformOps{
public:
PerformOps();
var1, var2, var3;
.
.
void start();
}
class MainWindow{
private:
Settings *set; //share pointers over all methods
PerformOps *op;
bool settings_not_clicked = false;
void saveChanges()
{
QSettings settings("my","app");
settings.setValue("value1", op->var1);
settings.setValue("value2", op->var2);
settings.setValue("value3", op->var3);
}
void loadChanges()
{
QSettings settings("my","app");
op->var1 = settings.value("value1");
op->var2 = settings.value("value2");
op->var3 = settings.value("value3");
}
void closeEvent(QCloseEvent *event)
{
event->ignore();
saveChanges();
event->accept();
}
void takeDataAndDoStuff() //This is always called
{
op = new PerformOps;
if(settings_not_clicked) {
loadChanges()
}
else {
op->var1 = 33;
op->var2 = "Geronimo!";
op->var3 = true;
}
op->start();
}
void getStuff_maybe_if_clicked() //This might not be always called
{
Settings *set = new Settings(this);
set->exec() //Pause parent, run Settings
settings_not_clicked = false;
}
Question: Is there a cleaner way to share data across classes without resorting to the dirty method of : op->var1 = set->var1;, taking into account that the set pointer might not always be initialised?
Well, the approach itself is not that bad, however there are some things you can improve.
First of all, if I understood you correctly, you want to pass the settings if they exist, and pass the default values if they don't. In this case you can utilize the constructor:
class PerformOps
{
public:
PerformOps( int v1 = 33, string v2 = "Geronimo!", bool v3 = true ): var1(v1), var2(v2), var3(v3)
{
}
<...>
}
Now, if you call the constructor as PerformOps(), the default values will be set. If you call it and feed it some of the values, it will use them:
<...>
PerformOps *op;
if(settings_not_clicked) {
op = new PerformOps( set->var1, set->var2, set->var3 );
}
else {
op = new PerformOps();
}
<...>
of course, if you don't want to do it via the cunstructor, you could just make a function and call it like "setData()" and use the same technique with default function parameters.
Now, as for the pointers. It is a good idea to always initialize pointers with NULL, or nullptr if you have c++0x. Also, when you delete the memory, assign the pointer to NULL or nullptr again. This way you will be able to always see if the pointer is valid by a simple check.
someClass * ptr = nullptr;
<...>
ptr = new someClass();
<...>
delete ptr;
ptr = nullptr;
UPD
I would suggest you to get rid of your Settings class, and just use QSettings directly. You will not need to mess with the pointers, and reading/writing to QSettings is very fast.
Now, also don't forget that you can use QSettings from the heap:
QSettings * settings = new QSettings();
If you want each settings set to have a "parent", you could just derive your Settings class from QSettings, and just add one parent field into it. This will keep all the functionality of QSettings, which is very convenient.
Actually, your approach is fine too, all you need to do is just to check if the pointers are valid.
Ok, so the problem is "I'm setting a pointer to a pointer that may or may not exist". In such a case Null Object pattern should help. This way you should be able getting rid of if-clause
Oh hang on.
I just realized that I can use QSettings between classes (which is what SingerOfTheFall was clearly hinting at)
So from my Settings.cpp class I can do:
QSettings sett("my");
sett.begingroup("my_groupname");
sett.setValue("value1", var1);
sett.setValue("value2", var2);
sett.setValue("value3", var3);
sett.endGroup();
and I can retrieve this same information from ANY CLASS (duh!) by doing:
someRandomClass.cpp:
QSettings settings("my");
settings.beginGroup("my_groupname");
myvar1 = settings.value("value1");
myvar2 = settings.value("value2");
myvar3 = settings.value("value3");
settings.endGroup();

templated abstract factory class with pooling

I'm working on a game engine component that handles events. What I'm trying to do is create a system that I can register new event types by name. The event manager will then hold a collection of event types and the factories to generate such an event type BUT the twist is that I want to make it used a pooling system such that I create an event, use it and then rather than deleting it, throw it into a list. Next time I create that event, rather than using the heap, I can just allocate from the pool.
SO given these hierarchy of event types...
struct TEvent
{
int nID;
int nTimeStamp;
};
struct TCollisionEvent : public TEvent
{
TEntity* pEntity1;
TEntity* pEntity2;
Vector3* pvecPoint;
};
I then created a smart factory which does this creation/recyling operation:
template <class BASE_CLASS>
class CSmartFactory
{
private:
typedef typename std::list<BASE_CLASS*> TBaseList;
typedef typename std::list<BASE_CLASS*>::iterator TBaseListItr;
TBaseList* m_plstPool;
public:
explicit CSmartFactory()
{
m_plstPool = NULL;
}
~CSmartFactory()
{
TBaseListItr itr;
if (m_plstPool)
{
for (itr = m_plstPool->begin(); itr != m_plstPool->end(); itr++)
{
BASE_CLASS* pEntity = *itr;
SAFE_DELETE(pEntity);
}
m_plstPool->clear();
SAFE_DELETE(m_plstPool);
}
}
bool Init(int nPoolSize)
{
bool bReturn = false;
do
{
m_plstPool = new TBaseList;
IwAssert(MAIN, m_plstPool);
while (nPoolSize--)
{
BASE_CLASS* pBaseObject = new BASE_CLASS;
IwAssert(MAIN, pBaseObject);
m_plstPool->push_back(pBaseObject);
}
bReturn = true;
} while(0);
return bReturn;
}
BASE_CLASS* Create()
{
BASE_CLASS* pBaseObject = NULL;
//
// grab a pre-made entity from the pool or allocate a new one
if (m_plstPool->size() > 0)
{
pBaseObject = m_plstPool->front();
m_plstPool->pop_front();
pBaseObject->Clear();
}
else
{
pBaseObject = new BASE_CLASS;
IwAssert(MAIN, pBaseObject);
}
return pBaseObject;
}
void Recycle(BASE_CLASS* pBaseObject)
{
m_plstPool->push_back(pBaseObject);
}
};
SO now I can do this:
CSmartFactory<TCollisionEvent>* pCollisionEventFactory = new CSmartFactory<TCollisionEvent>;
BUT what I want to do is have my event manager allow for dynamic event registration but that's where I run into my snag.
Ideally RegisterEvent will track the name and factory pointer in an stl::map or something but not quite sure how to get to that point. Maybe I've gone down the wrong path altogether.
This compiles
class TEventManager
{
public:
TEventManager();
~TEventManager();
bool RegisterEvent(char* pszEventName, CSmartFactory<TEvent>* pFactory);
};
Until you add
TEventManager::RegisterEvent("CollisionEvent", new CSmartFactory<TEntityCollisionEvent>);
So now I'm hopelessly trying to find a way to make this all work.
Anybody got some ideas here!?
Fred
I assume that you want to reuse events to avoid expensive heap malloc/free's?
I think the right answer here is not to convolute your code by writing your own structure for reusing objects, but to use a small-object allocator. As a start, it may be worth looking into boost::pool.
The two classes CSmartFactory<TEntityCollisionEvent> and CSmartFactory<TEvent> will be generated to something like
CSmartFactory_TEntityCollisionEvent
CSmartFactory_TEvent
They are actually two separate and unrelated classes. Trying to use them interchangeably would be unwise, although they behave the same (their type classes are polymorphic right).
Dynamic casting wont work, you could however try to use brute force casting:
TEventManager::RegisterEvent("CollisionEvent",
reinterpret_cast<CSmartFactory<TEvent>*>(new CSmartFactory<TEntityCollisionEvent>));
Warning: At your own risk! ;-)
OK so after a lot of head banging, I realized the solution is FAR simpler than what I was trying to pull off.
All the manager should care about is managing a TEvent*. Each TEvent has a unique hash value that makes it unique so when a new event is added both the string name and hash name of that even is stored. So from there I can add a pointer to any subclass so long as it's casted to TEvent.
I was making it FAR more complex than it needed to be.