1

Smoke provides an introspective C++ API wrapper to the Qt framework.
https://techbase.kde.org/Development/Languages/Smoke
https://techbase.kde.org/Development/Languages/Smoke/API_Documentation

SmokeC wraps the the Smoke wrapper with C functions to facilitate using the Smoke API from C.
https://github.com/pankajp/pysmoke/blob/master/smokec/smokec.cpp

Working example application in C (functionally identical to the C++ example in the first link above):
https://github.com/pankajp/pysmoke/blob/master/examples/hellowidget.c

I'm trying to modify this example application and add further calls to QWidget object methods, but I can't figure out how to call a method that takes a QString argument, such as setWindowTitle, whose signature is

void setWindowTitle(const QString &);

Here's my best guess:

/* Set the window title... Segfaults */    
methId = Smoke_findMethod(classId.smoke, "QWidget", "setWindowTitle$");
klass = Smoke_classes(classId.smoke)[classId.index];
meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
stack[1].s_voidp = (void*)"hello universe";
(*klass.classFn)(meth.method, widget, stack);

... but this segfaults. Can anyone advise how I should construct a QString argument using the SmokeC wrapper?

My complete modified example application follows.

/* 
 * File:   hellowidget.c
 * Author: pankaj
 *
 * Created on November 19, 2013, 2:41 PM
 */


#include <stdio.h>
#include <string.h>
#include "smokec.h"
#include "bindings.h"


/*
 * In a bindings runtime, this should return the classname as used
 * in the bindings language, e.g. Qt::Widget in Ruby or
 * Qyoto.QWidget in C# or QtGui.QWidget in python
 */
char *className(CSmokeBinding binding, Index classId) {
    return (char*) Smoke_classes(CSmoke_FromBinding(binding))[classId].className;
}


void deleted(CSmokeBinding binding, Index classId, void *obj) 
{
}

cbool callMethod(CSmokeBinding binding, Index method, void *obj, Stack args, cbool isAbstract)
{
    return 0;
}


int main(int argc, char **argv)
{
    /* Initialize the Qt SMOKE runtime. */
    init_qtcore_CSmoke();
    init_qtgui_CSmoke();

    CSmoke qtcore_smoke = qtcore_CSmoke();
    CSmoke qtgui_smoke = qtgui_CSmoke();

    /* Create a SmokeBinding for the Qt SMOKE runtime. */
    CSmokeBinding qtcoreBinding = SmokeBinding_new(qtcore_smoke, deleted, callMethod, className);
    CSmokeBinding qtguiBinding = SmokeBinding_new(qtgui_smoke, deleted, callMethod, className);

    /* Find the 'QApplication' class. */
    CModuleIndex classId = findClass("QApplication");
    /* find the methodId. we use a munged method signature, where
     * $ is a plain scalar
     * # is an object
     * ? is a non-scalar (reference to array or hash, undef) */
    CModuleIndex methId = Smoke_findMethod(classId.smoke, "QApplication", "QApplication$?");  // find the constructor

    /* Get the Smoke::Class */
    Class klass = Smoke_classes(classId.smoke)[classId.index];

    // findMethod() returns an index into methodMaps, which has
    // information about the classId, methodNameId and methodId. we
    // are interested in the methodId to get a Smoke::Method
    Method meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];

    StackItem stack[3];
    // QApplication expects a reference to argc, so we pass it as a pointer
    stack[1].s_voidp = &argc;
    stack[2].s_voidp = argv;
    // call the constructor, Smoke::Method::method is the methodId
    // specifically for this class.
    (*klass.classFn)(meth.method, 0, stack);

    // the zeroth element contains the return value, in this case the
    // QApplication instance
    void *qapp = stack[0].s_voidp;

    // method index 0 is always "set smoke binding" - needed for
    // virtual method callbacks etc.
    stack[1].s_voidp = qtguiBinding.binding;
    (*klass.classFn)(0, qapp, stack);



    // create a widget
    classId = findClass("QWidget");
    methId = Smoke_findMethod(classId.smoke, "QWidget", "QWidget");

    klass = Smoke_classes(classId.smoke)[classId.index];
    meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
    (*klass.classFn)(meth.method, 0, stack);

    void *widget = stack[0].s_voidp;
    // set the smoke binding
    stack[1].s_voidp = qtguiBinding.binding;
    (*klass.classFn)(0, widget, stack);

    /* Show the widget maximized.*/
    methId = Smoke_findMethod(classId.smoke, "QWidget", "showMaximized");
    meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
    (*klass.classFn)(meth.method, widget, 0);

    /* Raise the window to the foreground */
    methId = Smoke_findMethod(classId.smoke, "QWidget", "raise");
    meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
    (*klass.classFn)(meth.method, widget, 0);

    /* Set the modified indicator. */
    methId = Smoke_findMethod(classId.smoke, "QWidget", "setWindowModified$");
    meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
    stack[1].s_bool = 1;
    (*klass.classFn)(meth.method, widget, stack);

    /* Set the window title... Segfaults */    
    methId = Smoke_findMethod(classId.smoke, "QWidget", "setWindowTitle$");
    klass = Smoke_classes(classId.smoke)[classId.index];
    meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
    stack[1].s_voidp = (void*)"hello universe";
    (*klass.classFn)(meth.method, widget, stack);


    // we don't even need findClass() when we use the classId provided
    // by the MethodMap
    methId = Smoke_findMethod(qtgui_smoke, "QApplication", "exec");

    klass = Smoke_classes(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].classId];
    meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];

    // call QApplication::exec()
    (*klass.classFn)(meth.method, 0, stack);

    // store the return value of QApplication::exec()
    int retval = stack[0].s_int;

    // destroy the QApplication instance
    methId = Smoke_findMethod(qtgui_smoke, "QApplication", "~QApplication");

    meth = Smoke_methods(methId.smoke)[Smoke_methodMaps(methId.smoke)[methId.index].method];
    (*klass.classFn)(meth.method, qapp, 0);

    // destroy the smoke instance
    CSmoke_delete(qtgui_smoke);
    CSmoke_delete(qtcore_smoke);

    // return the previously stored value
    return retval;
}
zwol
  • 135,547
  • 38
  • 252
  • 361
pw222
  • 815
  • 1
  • 6
  • 11
  • 1
    What is unclear about that error message? This is not a Stack Overflow question either way. – László Papp Jul 12 '14 at 06:01
  • The error message isn't unclear -- it's just buggy. The single line of code in the post is indented. I even tried removing the line of code and the error message remained. I also tried surrounding the links in angle brackets. Nothing seemed to work. On your other note, I disagree with your assertion that this question is not suitable for SO. This is a pure programming question and surely has a simple answer. Thanks for your input! – pw222 Jul 12 '14 at 06:14
  • I think you misunderstood me; html is code, too. It is not indented. Also, I was referring to the current _inline_ question content. This is not a question. – László Papp Jul 12 '14 at 06:45
  • There's no html code in the question. As a last ditch effort to get rid of the message, I surrounded the urls in angle brackets. That's not html and it wasn't the cause of the SO bug. The current inline content points to the real content to bypass the SO bug. – pw222 Jul 12 '14 at 08:58
  • 1
    Well, good luck with this. – László Papp Jul 12 '14 at 08:59
  • You're trying to call a C++ function from C. That's your problem – Cole Tobin Jul 12 '14 at 13:54
  • 1
    I have edited your question into your question for you. I didn't have any problem with getting it to format correctly; if you continue to have problems with this, ask for help in chat *before* posting the question. We frown very much on pastebins, because we want every question to make sense years later, and pastebin data tends not to stick around. – zwol Jul 12 '14 at 13:54
  • It is not clear to me whether the "example application" is the *original* example application, or your modified version. If it is the original application, please edit the question again and replace it with the modified version. We need to see the modified version to have any hope of helping. – zwol Jul 12 '14 at 13:56
  • Zack, thanks for your help. I know this is a complicated question and the SO bug only further complicates it. In fact, I just tried editing the post to pare down the inline code to just the relevant portion and I got the same error! Oh well. Anyways, in regards to the actual question, on the KDE page about Smoke there is example code in C++. The guy who wrote "SmokeC" intended to allow you to use Smoke from C, and he transposed the KDE example code to C, using "SmokeC". I modified this latter example code by adding calls to additional Qt methods but I'm unsure how to pass string arguments. – pw222 Jul 12 '14 at 14:33
  • To clarify, the posted code is my modification of the "SmokeC" example C code. – pw222 Jul 12 '14 at 14:42
  • 1
    @pw222 If you put up another pastebin with the pared-down code and @-reply to me when you've done it, I can edit it into the question for you. (I think your browser might be misinterpreting SO's JavaScript or something - I've literally never seen that error.) Anyhow, my best guess as to your actual question is that you need to construct `QString` objects the same way you construct, say, `QApplication`. (And I have to say that this seems like a fragile and *painful* way to use Qt.) – zwol Jul 12 '14 at 16:57
  • @Zack thanks for the suggestion. That does make sense. And yes, it does seem like a painful way to use Qt. The idea is to eventually make a scripting language binding to painlessly use Qt from that language. I've got a long ways to go though. Here's the pared-down pastebin: http://pastebin.com/skLcjjSn My best guess on the SO issue is that it's some sort of spam-filter triggered by all the links, and SO is trying to disguise that as something else. Seems pretty silly if that is the case. I googled around and it looks like others have had this problem before. Thanks again for your help! – pw222 Jul 12 '14 at 20:18
  • No wonder your code segfaults, if you give it address of a C string, but it expects address (reference) of `QString` object. Anyway, if you managed to solve this (write code which creates `QString` object, and pass address of that), you could write an answer with the correct code snippet to do the call right. – hyde Jul 17 '14 at 18:10

0 Answers0