LinuxQuestions.org
Review your favorite Linux distribution.
Go Back   LinuxQuestions.org > Blogs > rainbowsally
User Name
Password

Notices



Rate this Entry

GUI-Interactive: Display a list of QT4 classes

Posted 05-05-2012 at 05:51 AM by rainbowsally
Updated 05-05-2012 at 06:00 AM by rainbowsally

Today's features:
  • A stand alone executable to list all of QT4's classes.
  • A first function for an interactive interpreter for QT funcs and classes
  • Another simple but interesting parse job.

Requres:
  • the libLQ download (or your own work-alike hack)

http://www.linuxquestions.org/questi...eration-34648/

We are delving into QT's system for reasons explained in earlier blog entries. When I tried early versions of QT4 it was at the bottom of the list of potential GUI's I was looking at. But the newer QT4 seems to be pretty good. Might even stick with it. :-)

For the interactive layer (communicating with the GUI system) one thing we may need is a list of all available classes and a 'word' we can use to get this information.

[From this list we'll be able search for classes, write includes and create objects to view their properties and methods, etc.]

We may need contexts like namespaces in the future and since C structs already incorporate this kind of encapsulation by way of '->' or '.' accessors (such as they are), let's borrow the '.' notation scheme, initially sans namespace context.

Here's the task.

Code:
list all classes:
  find all headers in /usr/include/Qt that contain "Q_<*>_EXPORT" and 
  extract the class name.
  display the list.
Code:
void classes_x_listAll(int argc, char** argv)
{
  // implementation details
}
The reason for the _x_ is to allow namespace prefixes like dots arrows or even "::".

The reason for the argc, argv parameters is to reuse existing traditions, import and export functions to and from stand-alone xecutables, and also allow for the eventual development of a bytecode interpreter which will be able to compile this function like this.

Code:
 : argc | function_address [ | param1 | param2 ... ] ;
If argc = 1, function address is param[0]. We can therefore also think of the argc value as a 'commandlength' in the bytecode compiler. Our first function definition will be this kind of function.

The execution loop therefor will set the function_address as argv (pointer to self) and the argc for this call is 1 (no additional parameters).

We also need a function to exit the interpeter loop, which we'll name "quit".

Code:
void quit(int argc, char** argv)
{
  // implementation details
}
It's in the 'root' context so it has no prefix.

We need to look through all the files in /usr/include/Qt for file's that contain lines that look like this:
[code] class Q_<...>_EXPORT %{ClassName}% ... // i.e., : public ParentName[code]


file: src/error_codes.dat
purpose: sync enum and string data for sequential tokens
Code:
////////////////////////////////////////////////////////////////////////
// This is a list of name/value pairs for loading constant error tokens
////////////////////////////////////////////////////////////////////////
// User defines LOAD_NV to either set the first or the second parameter
// in the enum or the string array.

LOAD_NV(ERROR_VERSION, "NO VERSION, JUST FOR TEST"),
LOAD_NV(ERROR_ARGC, "Wrong number of args"),
LOAD_NV(ERROR_ENDLIST, 0)

file: src/main.cpp
[code]
// generic C++ source template

#include <stdio.h> // printf(), FILE*, etc.
#include <malloc.h> // malloc(), free()
#include <string.h> // strcpy(), memcpy(), etc.
#include <stdlib.h> // exit()

// enable as needed
#include <LQ/LQ.h> // loads all libLQ headers

#define TESTING_LISTALL 1 /* includes main() function in compilation */

void dbg(){}

#define LOAD_NV(tokname, str) \
tokname

typedef enum
{
#include "error_codes.dat"
// ERROR_VERSION,
// ERROR_ARGC,
// ERROR_ENDIST
}ERROR_CODE;

#undef LOAD_NV

#define LOAD_NV(tokname, str) \
str

const char* error_string[] =
{
#include "error_codes.dat"
// "1.0",
// "Wrong number of args",
// 0 // ENDLIST
};

#undef LOAD_NV


void syntax_error(char** argv, ERROR_CODE code, const char* helpstr)
{
#ifndef HAVE_FUNC_LOOKUPS
// simulate a lookup table for function names
char* _func_name = (char*)"Unknown";
char* func_name_tbl[] = {_func_name, 0};
int func_index = 0;
#else
// TODO
#endif
if(code >= ERROR_ENDLIST)
{
fprintf(stderr,
"Uh... something's really screwy here.\n"
" Not even the error code is right in '%s'\n"
" Is this even the right version? (%s) -rs", __func__, error_string[0]);
// XBell();
exit(1);
}
fprintf(stderr, "In [%s]: %s\n", func_name_tbl[func_index], error_string
Code:
);
  if(helpstr)
    fprintf(stderr, "%s\n", helpstr);
  exit(code);
}

void parse_classnames(char*** pclassnames, char**filelist);

// subroutines are private and may have non-std parameter lists and 
// return values.  The pvt_ prefix isn't necessary, but don't use 
// _x_ anywhere in the name.  We may need that to generate jump addresses
// and name linkage later.

void pvt_listAll(char*** plist)
{
  slist_pipeRead(plist, "ls /usr/include/Qt/*.h 2>/dev/null");
}

void pvt_parse_classnames(char*** pclassnames, char** filelist);
    
void classes_x_listAll(int argc, char** argv)
{
  // implementation details
  if(argc != 1)
    syntax_error(argv, ERROR_ARGC, "Needs 0 args");
  // get list of headers in /usr/include/Qt and create list of 
  // find class strings
  char** filelist = slist_new();
  char** classnames = slist_new();
  pvt_listAll(&filelist);
  // for(char** ps = filelist; *ps; ps++) printf("%s\n", *ps);
  if(slist_count(filelist)) 
  {
    pvt_parse_classnames(&classnames, filelist);
    for(int i = 0; i < slist_count(classnames); i++)
      printf("%s\n", classnames[i]);
  }
  slist_delete(&filelist);
  slist_delete(&classnames);
}

static void pvt_parse_one_classname(char*** pclassnames, const char* filename)
{
  FILE* fp = fopen(filename, "r");
  char ibuf[256];
  char obuf[256];
  // simple parse for text 
  // class Q_GUI_EXPORT %{ClassName}% ... 

  const char* ip;
  char* op;
  if(!fp) return;
  while(!feof(fp))
  {
    *ibuf = 0;
    fgets(ibuf, 256, fp);
    if(*ibuf)
    {
      // simple parse job, let's do it inline
      do
      {
        ip = ibuf;
        op = obuf;
        
        // skip [whitespaces]
        while(*ip == ' ') ip++;
        
        // skip "class"
        if(strncmp(ip, "class", 5)) break;
        ip += 5;
        
        // skip [whitespaces]
        while(*ip == ' ') ip++;
        
        // skip "Q_"<*>_EXPORT"
        {
          bool state = false;
          do
          {
            if(strncmp(ip, "Q_", 2)) break;
            ip += 2;
            for(; *ip > ' '; ip++)
            {
              if(strncmp(ip, "_EXPORT", 7) == 0) 
              {
                ip += 7;
                state = true; 
                break;
              }
            }
          }while(0);
          if(!state)
            break;
        } // end "Q_"<*>_EXPORT"

        // skip [whitespaces]
        while(*ip == ' ') ip++;
        
        // copy classname
        while((*ip > ' ') && (*ip != ':'))
          *op++ = *ip++;
      }while(0);
      *op = 0;
      if(*obuf)
        slist_append(pclassnames, obuf);
    }
  }
  fclose(fp);
}

void pvt_parse_classnames(char*** pclassnames, char** filelist)
{
  int nfiles = slist_count(filelist);
  const char* filename;
  for(int i = 0; i < nfiles; i++)
  {
    filename = filelist[i];
    pvt_parse_one_classname(pclassnames, filename);
  }
}

#if TESTING_LISTALL
int main(int argc, char** argv)
{
  dbg();  
  classes_x_listAll(argc, argv);
  return 0;
}
#endif // TESTING_LISTALL

For the makefile, try 'mc2 -fetch LQ++'.

I used the C++ template but I then had to add an INCLUDE and a LIB and an LDFLAG.

Code:
PREFIX = $(HOME)/usr32
INCLUDE = -I $(SRCDIR) -I$(PREFIX)/include -I /usr/include 
LINK = g++ -m32 -o # LINK <output_file> ...
LDFLAGS = -lLQ
LIB = -L/usr/lib -L$(PREFIX)/lib
The above might get you rolling even if you don't use mc2. Good luck. :-)

The executable lists just under a thousand classes on my system.

Here's just the start and the end of the dump.
Code:
Q3Accel
Q3Action
Q3ActionGroup
  <snipping 905 class names>
QXmlSchema
QXmlSchemaValidator
QXmlSerializer
It looks pretty much like we might be able to write code to include the right header ala "#include <ClassName>" and create an object and then call it's metaObject() to get properties and methods either as an external temp app even if we can't get our interpreter to generate the object in a class factory of some kind. So one way or another, we'll be able to get this info for most if not all of these classes.

And this interpreter will carry around everything it needs to do its jobs because the exectuable and the lib are one and the same.

Next let's start playing with a byte code* compatible interpreter and see if we can actually execute this hair-brained plan.

----------------
* re. byte-code.
It may not end up being exactly byte-code because we can use hard coded direct addresses, which may be a easier to port back and forth to/from stand alone executables since we won't have that extra level of indirection to unravel.

The Computer Mad Science Team.

:-)
Posted in Uncategorized
Views 395 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 08:55 AM.

Main Menu
Advertisement

Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration