Search code examples
c++qtqtscript

Call object instance from C++ function called via script


I have a class named Jellyfish who use the singleton design pattern :

jellyfish.h

#ifndef JELLYFISH_H
#define JELLYFISH_H

#include <QHash>
#include <QScriptEngine>

class QScriptValue;

class Jellyfish : public QObject
{

    public:

         static Jellyfish * getInstance();
         static Jellyfish * instance;

    private:

        Jellyfish();

};

#ifndef

jellyfish.cpp

Jellyfish * Jellyfish::instance = NULL;

Jellyfish * Jellyfish::getInstance()
{

    if ( !Jellyfish::instance )
    {
        Jellyfish::instance = new Jellyfish();
    }

    return Jellyfish::instance;

}

When I am in main.cpp and make tests, I have no errors:

main.cpp

#include <QApplication>

#include "jellyfish.h"

class Jellyfish;

int main( int argc, char *argv[] )
{

    QApplication app( argc, argv );

    Jellyfish *toto = Jellyfish::getInstance();
    Jellyfish *toto2 = Jellyfish::getInstance();
    Jellyfish *toto3 = Jellyfish::getInstance();

    return app.exec();

}

But I want to use some static methods in Jellyfish in external QScript files :

jellyfish.h

private:

    static QScriptValue set( QScriptContext *context, QScriptEngine *engine );
    QScriptEngine *script_engine;

jellyfish.cpp

Jellyfish::Jellyfish()
{

    script_engine = new QScriptEngine;

    /* ... */

    initScriptEngine();

}

void Jellyfish::initScriptEngine()
{

    QScriptValue object = script_engine->newQObject( this );

    object.setProperty( "set", script_engine->newFunction( set ) );

    script_engine->globalObject().setProperty( "jellyfish", object );

}

QScriptValue Jellyfish::set( QScriptContext *context, QScriptEngine *engine )
{

    // I have to load instance because I am in a static method.
    // But this is where the application loop endlessly.
    Jellyfish *jellyfish = Jellyfish::getInstance();
    return true;

}

And finaly the parsed script:

jellyfish.set( "line_numbers",  true );

Problem

When I run the application, getInstance() always creates a new Jellyfish instance. But the real problem is that on the output with debug (qDebug() << "test";) I can see that the application loop on Jellyfish::getInstance(); until I get a sgmentation fault.

Could someone help me to understand this ?


Solution

  • It is because your Jellyfish constructor call itself recursively!

    Break your program in debugger and you will see backtrace like this:

       ...
       Jellyfish::Jellyfish()
       Jellyfish::getInstance()
       Jellyfish::set
       Jellyfish::initScriptEngine() 
       Jellyfish::Jellyfish()
       Jellyfish::getInstance()
    

    The problem is that your constructor calls Jellyfish::getInstance(), but when object is constructed the Jellyfish::instance is still NULL:

    Jellyfish * Jellyfish::getInstance()
    {
    
        if ( !Jellyfish::instance )
        {
            // this line
            // Jellyfish::instance = new Jellyfish();
            // is equivalent to
            Jellyfish * temp = new Jellyfish(); // *
            Jellyfish::instance = temp; // not NULL after construction
        }
    
        return Jellyfish::instance;
    
    }
    

    If you call getInstance() from your constructor then it will always result in infinite recursion (until segfault). The only one solution is to call Jellyfish::initScriptEngine() not from your constructor - but explicit:

       Jellyfish::getInstance()->initScriptEngine();