Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Publicaciones sobre python (publicaciones antiguas, página 13)

Python Contest

There is a python con­test at http://www.py­con­test.net/

The task is writ­ing the short­est pro­gram to drive a sev­en-seg­ment LCD thingy.

I have no hope of win­ning, but here's a help­ful hin­t:

If your code is any longer than this (191 chars), it will not win ;-)

a=' _ '
b='|_|'
c='   '
d='  |'
e=' _|'
f='|_ '
g='| |'
v='agbcddaefaeecbdafeafbaddabbabe'
def seven_seg(x):
        return '\n'.join([eval('+'.join([v[int(l)*3+i]for l in x]))for i in 0,1,2])+'\n'

Note: I edit­ed this item way too many times al­ready ;-)

And yes, I can save two char­ac­ters mov­ing the re­turn up.

A much uglier, yet much short­er (151) ver­sion:

def seven_seg(x):return''.join([''.join(['|    ||__  __ || |  |'[int('a302ho6nqyp9vxvpeow',36)/10**(int(l)*3+u)%10::7]for l in x])+'\n'for u in 0,1,2])

And yes, that pret­ty much proves you can write ug­ly python. And I am giv­ing up. A short­er ver­sion prob­a­bly in­volves a dif­fer­ent al­go­rith­m, and I can't find any.

I am par­tic­u­lar­ly proud of sav­ing one char­ac­ter by writ­ing 104004334054154302114514332064 as in­t('a302ho6n­qyp9vxvpe­ow',36).

Al­so in­ter­est­ing is that the num­ber I was us­ing there orig­i­nal­ly start­ed with 4 and was ex­act­ly the same length writ­ten both ways ;-)

Since that num­ber is pret­ty ar­bi­trary (it's an in­dex ta­ble in­to the "graph­ic­s" ar­ray), I just shuf­fled the 1 and the 4. The 0 would have been bet­ter but then it did­n't work, of course :-)

Forgotten Language: Jorf could have been Python (or Ruby)

A long time ago, there was no In­ter­net.

Ok, there was an in­ter­net, but I lived out­side of it. It was 1992 or 1993, and I on­ly saw my first web­page and send my first email in 1995. And I was per­haps the third per­son to have an ac­count on a per­ma­nent­ly in­ter­net-­con­nect­ed box in a 150 km ra­dius.

But that did­n't mean I had no ac­cess to in­ter­net stuff! What I did was buy CDs con­tain­ing mir­rors of repos­i­to­ries like Sim­tel.net (it was a sin­gle CD, too!) and in there you could find hun­dreds of pro­gram­s.

Most of them share­ware, most of them crap, but ev­ery once in a while, there was some­thing cool, like DJGPP (a whole gcc suite for DOS! A re­al C com­pil­er! For Free!)

At the time, I had a side job writ­ing da­ta en­try soft­ware for sta­tis­tics stu­dents. They were sim­ple pro­grams that showed a for­m, where da­ta was load­ed, then it did some sim­ple ma­nip­u­la­tions of the da­ta.

The nat­u­ral lan­guage for that was some­thing like Clip­per or DBase, but I did­n't have ac­cess to them (or a way to learn it. Re­mem­ber, no In­ter­net).

On one of those Sim­tel CDs I found Jor­f. (Josephine's Recipe Fil­er). It was a OO lan­guage, with an in­ter­preter for DOS or Win­dows, and it sup­port­ed stuff that was re­al­ly ad­vanced for the time, and it made my cod­ing a lot sim­pler.

Out of nos­tal­gy, I down­load­ed a copy (yes, it is still there), and ran it in Dos­BOX (yes, it still work­s), to check if it was as good as I re­mem­bered.

You know what? It is.

In fac­t, if it had come out 2 or three years lat­er, and as free soft­ware in­stead of share­ware... I think it would have been big.

Here are some high­lights og the lan­guage:

  • OOP

  • Has in­­te­­grat­ed win­­dow­ing tool­k­it (for DOS and Win­­dows)

  • It had an in­­ter­ac­­tive hy­per­­tex­t/win­­dow­ing tu­­to­ri­al writ­ten in it­­self. In 1993.

  • It looks like a cousin of Python. A freaky cous­ing, though.

    1. Com­­­ments start with |

    2. Strings lim­it­ed with sin­­­gle or dou­ble quotes

    3. Au­­­to­­­mat­ic type con­ver­­­sions

    4. In­­­ten­­­ta­­­tion con­trols flow :-)

    5. No de­­­clared da­­­ta types

    6. In­­­te­­­grat­ed ed­i­­­tor and de­bug­ger

Sam­ple Hel­lo World:

Demo:Start
  Msg:Add ("Quick Demonstration","Ok")
    Sure you can say "Hello World" in one line of
    C code. But how many punctuation characters
    are required to display a dialog box like this?
  Return (Ok)

That piece of code showed a win­dow with the mes­sage in it, and a Ok but­ton.

The funky thing is: in the tu­to­ri­al, you saw the in­te­grat­ed ed­i­tor open, and the text of the ex­am­ple start to ap­pear, and then it ran.

That looked like mag­ic at the time :-)

The tool­kit sup­port­ed ra­dio but­ton­s, check­box­es, text en­tries, all the ba­sic­s, and it was a thou­sand times eas­i­er than what Tur­bo Pas­cal or Tur­bo C guys bat­tled with at the time.

The au­thor was Way­land Brun­s. He lived, in 1993, in Colton, Ore­gon.

He lat­er seems to have be­come CTO of a com­pa­ny that de­signs sync soft­ware for Gold­mine, Lo­tus and oth­er such things.

So, he be­came a suit ;-). How­ev­er, he was once a guy that wrote, in his soft­ware's man­u­al, things like:

JORF Com­pa­ny is just me, Way­land Brun­s. I have been work­ing on JORF for six years, and ran out of mon­ey three years ago.

Or:

JOR­F(R) is a new com­put­er lan­guage. JORF was cre­at­ed by a Grun­t-pro­gram­mer frus­trat­ed by low lev­el math based com­put­er lan­guages that are in­ap­propi­ate for busi­ness da­ta pro­cess­ing.

And you know what? It was the right idea. If he start­ed Jorf in 1987, that means he start­ed it around the same time Perl 1.0, (and the syn­tax is much nicer ;-). He start­ed it around the same time Gui­do start­ed Python.

Here's a toast to JOR­F, which could have been Per­l, or Python, or Ru­by. But was not.

Bound by Smoke I

This is what I un­der­stood of Smoke so far. I may be way of­f, since it is C++ sor­cery of a high­er lev­el than I'm used to, but I re­al­ly think I am get­ting the hang of it (and a bunch of thanks to Richard Dale and Ash­ley Win­ters who are the ones that made me un­der­stand so far. Any mis­takes a re my fault, any good thing is theirs ;-).

This piece is on­ly half of the sto­ry, though. Maybe one third.

Concept

Since Smoke's goal is to help you write bind­ings for lan­guages oth­er than C++, it pro­vides a way to ac­cess Qt's API. The orig­i­nal thing about Smoke is that it does so by pro­vid­ing you with a small­er, more dy­nam­ic API that maps on­to Qt's.

You could write a Qt C++ pro­gram us­ing Smoke as the API in­stead of us­ing Qt's. In fac­t, you can see it here writ­ten by Ash­ley Win­ter­s.

I had to re­name it hel­lo.cpp to make it work be­cause that looks like C but may not re­al­ly be C ;-)

As you can see, the Smoke ver­sion is quite a bit more com­plex than the Qt one. But that's ok, re­mem­ber that the goal is a bind­ing, which means that what you need to make your life sim­pler is less API... which is what makes the pro­gram more ver­bose.

Let's ex­am­ine the Smoke hel­lo.cpp in de­tail.

One key point is call­Method:

// call obj->method(args)
void callMethod(Smoke *smoke, void *obj, Smoke::Index method, Smoke::Stack args) {
        Smoke::Method *m = smoke->methods + method;
        Smoke::ClassFn fn = smoke->classes[m->classId].classFn;
        fn(m->method, obj, args);
}

If you have pro­grammed in Python or Ru­by or a num­ber of oth­er dy­nam­ic lan­guages you may guess what this does al­ready.

It takes as ar­gu­ments a num­ber of things which still have to be ex­plained but the main gist is there is an ob­jec­t, there is a method, there are args, and it ends call­ing ob­j->method­(args).

The first ar­gu­ment is a Smoke point­er , which is the big ob­ject in the Smoke li­brary, cre­at­ed in our pro­gram by the init_smoke func­tion.

A Smoke ob­ject is a strange beast. It con­tains a de­scrip­tion (in this case, be­cause we got qt_smoke) for the whole Qt API.

You can find class­es in it in­dexed by their names, and meth­ods for each class in­dexed by their names and types of ar­gu­ments.

That is why you can have a gener­ic call­Method that will work for any kind of ob­ject and for any method in the class, be­cause all of them are some­where in the Smoke ob­jec­t.

The sec­ond ar­gu­ment is void *obj which is the ob­ject it­self we are ma­nip­u­lat­ing. So, if you are try­ing to call QLa­bel::set­Tex­t, it will have to be a Qla­bel* cast­ed as void*.

In the hel­lo.cpp ex­am­ple, we even cre­ate these ob­jects us­ing Smoke's API (see lat­er).

The third ar­gu­ment is a Smoke::In­dex which is what Smoke us­es to find the re­quest­ed method in its method ta­ble. This In­dex we get us­ing the get­Method func­tion, which is the sec­ond key piece of code:

// given class-name and mangled function-name, return an unambiguous method ID
Smoke::Index getMethod(Smoke *smoke, const char* c, const char* m) {
        Smoke::Index method = smoke->findMethod(c, m);
        Smoke::Index i = smoke->methodMaps[method].method;
        if(i <= 0) {
                // ambiguous method have i < 0; it's possible to resolve them, see the other bindings
                fprintf(stderr, "%s method %s::%s\n",
                i ? "Ambiguous" : "Unknown", c, m);
                exit(-1);
        }
        return i;
}

Here is an ex­am­ple of a get­Method cal­l, where we are get­ting QAp­pli­ca­tion::set­Main­Wid­get

Smoke::Index method = getMethod(smoke, "QApplication", "setMainWidget#");

As you can see, we search for the method us­ing strings of the class name and method name. Ex­capt for that pesky # at the end of set­Main­Wid­get#.

That is a ba­sic ar­gu­men­t-­type­-­man­gling scheme, since there can be more than one QAp­pli­ca­tion::set­Main­Wid­get on the Qt side of the fence, we are say­ing we want the one that has an ob­ject as the first and on­ly ar­gu­men­t. Here is the key to the man­gling tak­en from smoke.h:

* The munging works this way:
* $ is a plain scalar
* # is an object
* ? is a non-scalar (reference to array or hash, undef)
*
* e.g. QApplication(int &, char **) becomes QApplication$?

I am not yet com­plete­ly clear on how this is enough to do all the work (for ex­am­ple, what hap­pens if you have two meth­ods that take dif­fer­ent ob­jects as on­ly ar­gu­men­t?) but it's what I saw :-)

The last ar­gu­men­t, args is of Smoke::S­tack type, and it's the tricky one, at least for me.

Here's how it's used in the pre­vi­ous ex­am­ple, QAp­pli­ca­tion::set­Main­Wid­get

// qapp->setMainWidget(l)
Smoke::Index method = getMethod(smoke, "QApplication", "setMainWidget#");
Smoke::StackItem args[2];
smokeCast(smoke, method, args, 1, l, "QLabel");
smokeCastThis(smoke, method, args, qapp, "QApplication");
callMethod(smoke, args[0].s_class, method, args);

A Smoke::S­tack is a way to pass the ar­gu­ments to be used with the method get­Method gave us.

We first cre­ate an ar­ray of 2 Stack­Item­s:

Smoke::StackItem args[2];

Then we as­sign a val­ue to the sec­ond of them:

smokeCast(smoke, method, args, 1, l, "QLabel");

Here l is a point­er to a QLa­bel. ( Ok, it is re­al­ly de­clared as a void* be­cause, re­mem­ber, we are not us­ing the Qt API, so we have no clue what a QLa­bel is ;-) and what we are do­ing is stor­ing in args[1] a cast­ed ver­sion of l.

The ex­act de­tails of why you have to pass smoke and method are not that im­por­tan­t, and they seem pret­ty in­volved, so I won't try to go there, at least not yet. This has to be done for each ar­gu­ment for the method.

Then we have a sim­i­lar, yet dif­fer­ent line:

smokeCastThis(smoke, method, args, qapp, "QApplication");

This puts the qapp void * in args[0], cast­ed to QAp­pli­ca­tion. There are tricky C++ rea­sons why this is done slight­ly dif­fer­ent here than on smoke­Cast, which I am not 100% sure I get right, so I will keep qui­et ;-)

This spe­cial case is on­ly for the ob­ject to which the method be­longs (the this ob­jec­t).

Here is the code for smoke­Cast and smoke­Cast­This

// cast argument pointer to the correct type for the specified method argument
// args[i].s_class = (void*)(typeof(args[i]))(className*)obj
void smokeCast(Smoke *smoke, Smoke::Index method, Smoke::Stack args, Smoke::Index i, void *obj, const char *className) {
        // cast obj from className to the desired type of args[i]
        Smoke::Index arg = smoke->argumentList[
                smoke->methods[method].args + i - 1
        ];
        // cast(obj, from_type, to_type)
        args[i].s_class = smoke->cast(obj, smoke->idClass(className), smoke->types[arg].classId);
}

// cast obj to the required type of this, which, dur to multiple-inheritance, could change the pointer-address
// from the one returned by new. Puts the pointer in args[0].s_class, even though smoke doesn't do it that way
void smokeCastThis(Smoke *smoke, Smoke::Index method, Smoke::Stack args, void *obj, const char *className) {
        args[0].s_class = smoke->cast(obj, smoke->idClass(className), smoke->methods[method].classId);
}

But where did we get l or qap­p? You can use these same mech­a­nisms to cre­ate an ob­jec­t:

void *qapp;
{
    // new QApplication(argc, argv)
    Smoke::Index method = getMethod(smoke, "QApplication", "QApplication$?");
    Smoke::StackItem args[3];
    args[1].s_voidp = (void*)&argc;
    args[2].s_voidp = (void*)argv;
    callMethod(smoke, 0, method, args);

    qapp = args[0].s_class;
}

You get QAp­pli­ca­tion::QAp­pli­ca­tion(s­calar,un­de­f) which should hope­ful­ly map to QAp­pli­ca­tion::QAp­pli­ca­tion(argc,argv). You cre­ate a Smoke::S­tack of 3 item­s. The first is un­set be­cause this is a con­struc­tor, so it has no this yet, and the oth­er two are argc and argv.

Then you call it through call­Method, and you get the re­sult­ing ob­ject via args[0].s_­class.

Lat­er you re­peat this sort of op­er­a­tion for ev­ery method call you wan­t, and you got your­self an ap­pli­ca­tion.

The binding side of things

So, how do you use this to bind your lan­guage to Qt?

Well, you will have to cre­ate an ob­ject in your lan­guage called, for ex­am­ple, QAp­pli­ca­tion, and map the "miss­ing method" mech­a­nism (y­our script­ing lan­guage prob­a­bly has one) which is used when a method is un­de­fined so that some­how it finds the right Smoke method (man­gling the name cor­rect­ly should be the hard part), then cre­ate the Smoke::S­tack with the passed ar­gu­ments, cal­l, get the re­sult, wrap it in some lan­guage con­struct you can use of the side of your lan­guage, and pass it back.

It looks in­volved, and I am sure it's trick­y, but at least it on­ly has to be done once un­like on tra­di­tion­al bind­ings, where you had to do it for ev­ery class and for ev­ery method.

The tra­di­tion­al so­lu­tion was to au­to­mat­i­cal­ly gen­er­ate the code for such wrap­ping (like SWIG does). I think Smoke is less er­ror prone.

If I keep on un­der­stand­ing things, there may be a sec­ond part of this ar­ti­cle, ex­plain­ing the Smoke­Bind­ing class, and per­haps a short ex­am­ple of how to bind Qt in­to a lan­guage (Io is a strong can­di­date).

The reg­u­lar­i­ty of Io's syn­tax is prob­a­bly go­ing to make the bind­ing sim­pler than most.

Ok, so, I am a lazy guy

I just re­al­ized I have not learned a whole new re­al lan­guage in al­most 5 years.

I have learned a few di­alect­s, some mi­nor stuff to work around or through an ob­sta­cle in some pro­jec­t, but I have not writ­ten a whole pro­gram in any­thing but python since 2001.

So, since I don't want my brain to turn in­to mues­li, I will now pro­ceed to learn Io .

Nice OO lan­guage, pro­to­type­-based, which is some­thing I haven't run in­to out­side of JS, I think, a sort of LISPy fla­vor, with a dash of smalltalk for pi­quan­cy.

Maybe I will get re­al­ly bored with it, maybe not :-)

And yes, the pre­vi­ous post was about hav­ing the idea of hack­ing a Qt bind­ing for it, but it's prob­a­bly not doable right now, since ex­tend­ing the lan­guage is pret­ty much un­doc­u­ment­ed, and ex­tend­ing it in C++ is un­heard of.

To top that, it us­es con­tin­u­a­tion­s, which prob­a­bly cause all kinds of hell on Qt's mem­o­ry man­age­ment :-(

A simple question

I was try­ing to do some­thing weird: take one of my PyQt prog­gies, and com­pile it in­to a stand­alone bi­na­ry us­ing Jython, koala/qt­ja­va and gjc.

Sad­ly, it does­n't work :-(

Has any­one ev­er got­ten a rather re­cent gjc and jython to work to­geth­er?

The best I can get is this:

[ralsina@monty jython]$ gcj -fjni /usr/lib/lib-org-*.so.* --classpath=/usr/java/j2sdk1.4.2_08/jre/lib/ --main=org.python.util.jython /usr/share/java/dom3-xerces-j2.jar /usr/share/java/ant.jar /usr/share/java/servlet.jar /usr/share/java/oro.jar /usr/share/java/jython.jar  -o jython
org/apache/html/dom/HTMLAnchorElementImpl.java:0: error: cannot find file for class   org.w3c.dom.events.EventTarget
org/apache/html/dom/HTMLAnchorElementImpl.java:0: error: cannot find file for class org.w3c.dom.TypeInfo
org/apache/html/dom/HTMLAnchorElementImpl.java:0: error: cannot find file for class org.w3c.dom.html.HTMLElement
org/apache/html/dom/HTMLAnchorElementImpl.java:0: error: cannot find file for class org.w3c.dom.html.HTMLAnchorElement
org/apache/html/dom/HTMLAnchorElementImpl.class:0: confused by earlier errors, bailing out

This is us­ing gc­c-­java-3.4.3-22.1 and jython-2.2-0.a0.2jpp

I mean, this is com­plete­ly un­nec­es­sary, but it would be a nice hack :-)


Contents © 2000-2023 Roberto Alsina