filter declarations in C++, improve luadoc

This commit is contained in:
Robin Gareus 2016-03-25 16:31:16 +01:00
parent c8b7d70ffa
commit dd27620566
3 changed files with 198 additions and 53 deletions

View File

@ -14,6 +14,8 @@ echo "# analyzing source.. -> $TMPFILE"
$LLVMINCLUDE -I /usr/include/linux \
-I libs/ardour -I libs/pbd -I libs/lua -I gtk2_ardour -I libs/timecode \
-I libs/ltc -I libs/evoral \
-X "_" -X "::" -X "sigc" -X "Atk::" -X "Gdk::" -X "Gtk::" -X "Gio::" \
-X "Glib::" -X "Pango::" -X "luabridge::" \
libs/ardour/ardour/* libs/pbd/pbd/* \
gtk2_ardour/*.h \
/usr/include/cairomm-1.0/cairomm/context.h \
@ -28,22 +30,12 @@ php << EOF
\$api = array ();
foreach (json_decode (\$json, true) as \$a) {
if (!isset (\$a['decl'])) { continue; }
if (empty (\$a['decl'])) { continue; }
if (\$a['decl'] == '::') { continue; }
if (substr (\$a['decl'], 0, 1) == '_') { continue; }
if (substr (\$a['decl'], 0, 2) == '::') { continue; }
if (substr (\$a['decl'], 0, 4) == 'sigc') { continue; }
if (substr (\$a['decl'], 0, 5) == 'Atk::') { continue; }
if (substr (\$a['decl'], 0, 5) == 'Gdk::') { continue; }
if (substr (\$a['decl'], 0, 5) == 'Gtk::') { continue; }
if (substr (\$a['decl'], 0, 5) == 'Gio::') { continue; }
if (substr (\$a['decl'], 0, 6) == 'Glib::') { continue; }
if (substr (\$a['decl'], 0, 7) == 'Pango::') { continue; }
if (substr (\$a['decl'], 0, 11) == 'luabridge::') { continue; }
\$a['decl'] = str_replace ('size_t', 'unsigned long', \$a['decl']);
\$a['decl'] = str_replace ('uint32_t', 'unsigned int', \$a['decl']);
\$a['decl'] = str_replace ('int32_t', 'int', \$a['decl']);
\$a['decl'] = str_replace ('framepos_t', 'long', \$a['decl']);
\$a['decl'] = str_replace ('framecnt_t', 'long', \$a['decl']);
\$a['decl'] = str_replace ('frameoffset_t', 'long', \$a['decl']);
\$a['decl'] = str_replace ('int64_t', 'long', \$a['decl']);
\$a['decl'] = str_replace ('uint8_t', 'unsigned char', \$a['decl']);
@ -57,7 +49,7 @@ foreach (json_decode (\$json, true) as \$a) {
\$a['decl'] = str_replace ('const unsigned long', 'unsigned long', \$a['decl']);
\$canon = str_replace (' *', '*', \$a['decl']);
\$api[\$canon] = \$a;
}
}
\$jout = array ();
foreach (\$api as \$k => \$a) {
\$jout[] = \$a;

View File

@ -23,9 +23,49 @@
#include <cstring>
#include <sstream>
#include <iomanip>
#include <map>
#include <clang-c/Index.h>
#include <clang-c/Documentation.h>
struct dox2js {
dox2js () : clang_argc (2), clang_argv (0), excl_argc (0), excl_argv (0)
{
excl_argv = (char**) calloc (1, sizeof (char*));
clang_argv = (char**) malloc (clang_argc * sizeof (char*));
clang_argv[0] = strdup ("-x");
clang_argv[1] = strdup ("c++");
}
~dox2js () {
for (int i = 0; i < clang_argc; ++i) {
free (clang_argv[i]);
}
for (int i = 0; excl_argv[i]; ++i) {
free (excl_argv[i]);
}
free (clang_argv);
free (excl_argv);
}
void add_clang_arg (const char *a) {
clang_argv = (char**) realloc (clang_argv, (clang_argc + 2) * sizeof (char*));
clang_argv[clang_argc++] = strdup ("-I");
clang_argv[clang_argc++] = strdup (a);
}
void add_exclude (const char *a) {
excl_argv = (char**) realloc (excl_argv, (excl_argc + 2) * sizeof (char*));
excl_argv[excl_argc++] = strdup (a);
excl_argv[excl_argc] = NULL;
}
int clang_argc;
char** clang_argv;
int excl_argc;
char** excl_argv;
std::map <std::string, std::string> results;
};
static const char*
kind_to_txt (CXCursor cursor)
{
@ -69,20 +109,37 @@ escape_json (const std::string &s)
return o.str ();
}
static void recurse_parents (CXCursor cr) {
static std::string
recurse_parents (CXCursor cr) {
std::string rv;
CXCursor pc = clang_getCursorSemanticParent (cr);
if (CXCursor_TranslationUnit == clang_getCursorKind (pc)) {
return;
return rv;
}
if (!clang_Cursor_isNull (pc)) {
recurse_parents (pc);
printf ("%s::", clang_getCString (clang_getCursorDisplayName (pc)));
rv += recurse_parents (pc);
rv += clang_getCString (clang_getCursorDisplayName (pc));
rv += "::";
}
return rv;
}
static bool
check_excludes (const std::string& decl, CXClientData d) {
struct dox2js* dj = (struct dox2js*) d;
char** excl = dj->excl_argv;
for (int i = 0; excl[i]; ++i) {
if (decl.compare (0, strlen (excl[i]), excl[i]) == 0) {
return true;
}
}
return false;
}
static enum CXChildVisitResult
traverse (CXCursor cr, CXCursor /*parent*/, CXClientData)
traverse (CXCursor cr, CXCursor /*parent*/, CXClientData d)
{
struct dox2js* dj = (struct dox2js*) d;
CXComment c = clang_Cursor_getParsedComment (cr);
if (clang_Comment_getKind (c) != CXComment_Null
@ -90,45 +147,48 @@ traverse (CXCursor cr, CXCursor /*parent*/, CXClientData)
&& 0 != strlen (kind_to_txt (cr))
) {
printf ("{ \"decl\" : \"");
recurse_parents (cr);
// TODO: resolve typedef enum { .. } name;
// use clang_getCursorDefinition (clang_getCanonicalCursor (cr)) ??
printf ("%s\",\n", clang_getCString (clang_getCursorDisplayName (cr)));
std::string decl = recurse_parents (cr);
decl += clang_getCString (clang_getCursorDisplayName (cr));
if (clang_Cursor_isVariadic (cr)) {
printf (" \"variadic\" : true,\n");
if (decl.empty () || check_excludes (decl, d)) {
return CXChildVisit_Recurse;
}
printf (" \"kind\" : \"%s\",\n", kind_to_txt (cr));
std::ostringstream o;
o << "{ \"decl\" : \"" << decl << "\",\n";
if (clang_Cursor_isVariadic (cr)) {
o << " \"variadic\" : true,\n";
}
CXSourceLocation loc = clang_getCursorLocation (cr);
CXFile file; unsigned line, col, off;
clang_getFileLocation (loc, &file, &line, &col, &off);
printf (" \"src\" : \"%s:%d\",\n",
clang_getCString (clang_getFileName (file)), line);
o << " \"kind\" : \"" << kind_to_txt (cr) << "\",\n"
<< " \"src\" : \"" << clang_getCString (clang_getFileName (file)) << ":" << line << "\",\n"
<< " \"doc\" : \"" << escape_json (clang_getCString (clang_FullComment_getAsHTML (c))) << "\"\n"
<< "},\n";
printf (" \"doc\" : \"%s\"\n",
escape_json (clang_getCString (clang_FullComment_getAsHTML (c))).c_str ());
printf ("},\n");
dj->results[decl] = o.str ();
}
return CXChildVisit_Recurse;
}
static void
process_file (int argc, char **args, const char *fn)
process_file (const char* fn, struct dox2js *dj)
{
CXIndex index = clang_createIndex (0, 0);
CXTranslationUnit tu = clang_createTranslationUnitFromSourceFile (index, fn, argc, args, 0, 0);
CXTranslationUnit tu = clang_createTranslationUnitFromSourceFile (index, fn, dj->clang_argc, dj->clang_argv, 0, 0);
if (tu == NULL) {
fprintf (stderr, "Cannot create translation unit for src: %s\n", fn);
return;
}
clang_visitChildren (clang_getTranslationUnitCursor (tu), traverse, 0);
clang_visitChildren (clang_getTranslationUnitCursor (tu), traverse, (CXClientData) dj);
clang_disposeTranslationUnit (tu);
clang_disposeIndex (index);
@ -138,23 +198,23 @@ static void
usage (int status)
{
printf ("doxy2json - extract doxygen doc from C++ headers.\n\n");
fprintf (stderr, "Usage: dox2json [-I path]* <filename> [filename]*\n");
fprintf (stderr, "Usage: dox2json [-I path]* [-X exclude]* <filename> [filename]*\n");
exit (status);
}
int main (int argc, char **argv)
int main (int argc, char** argv)
{
int cnt = 2;
char **args = (char**) malloc (cnt * sizeof (char*));
args[0] = strdup ("-x");
args[1] = strdup ("c++");
struct dox2js dj;
bool report_progress = false;
int c;
while (EOF != (c = getopt (argc, argv, "I:"))) {
while (EOF != (c = getopt (argc, argv, "I:X:"))) {
switch (c) {
case 'I':
args = (char**) realloc (args, (cnt + 2) * sizeof (char*));
args[cnt++] = strdup ("-I");
args[cnt++] = strdup (optarg);
dj.add_clang_arg (optarg);
break;
case 'X':
dj.add_exclude (optarg);
break;
case 'h':
usage (0);
@ -168,16 +228,26 @@ int main (int argc, char **argv)
usage (EXIT_FAILURE);
}
printf ("[\n");
const int total = (argc - optind);
if (total > 6) {
report_progress = true;
}
for (int i = optind; i < argc; ++i) {
process_file (cnt, args, argv[i]);
process_file (argv[i], &dj);
if (report_progress) {
fprintf (stderr, "progress: %4.1f%% [%4d / %4d] decl: %ld \r",
100.f * (1.f + i - optind) / (float)total, i - optind, total,
dj.results.size ());
fflush (stderr);
}
}
printf ("[\n");
for (std::map <std::string, std::string>::const_iterator i = dj.results.begin (); i != dj.results.end (); ++i) {
printf ("%s\n", (*i).second.c_str ());
}
printf ("{} ]\n");
for (int i = 0; i < cnt; ++i) {
free (args[i]);
}
free (args);
return 0;
}

View File

@ -601,10 +601,15 @@ h2.enum { background-color: #aaaaaa; }
h2.pointerclass { background-color: #eeaa66; }
h2.array { background-color: #66aaee; }
h2.opaque { background-color: #6666aa; }
p { text-align: justify; }
p.cdecl { text-align: right; float:right; font-size:90%; margin:0; padding: 0 0 0 1em;}
ul.classindex { columns: 2; -webkit-columns: 2; -moz-columns: 2; }
div.clear { clear:both; }
p.classinfo { margin: .25em 0;}
div.code { width:80%; margin:.5em auto; }
div.code div { width:45%; }
div.code pre { line-height: 1.2em; margin: .25em 0; }
div.code samp { color: green; font-weight: bold; background-color: #eee; }
div.classdox { padding: .1em 1em;}
div.classdox p { margin: .5em 0 .5em .6em;}
div.classdox p { margin: .5em 0 .5em .6em;}
@ -615,7 +620,7 @@ table.classmembers th { text-align:left; border-bottom:1px solid black; pad
table.classmembers td.def { text-align:right; padding-right:.5em; white-space: nowrap;}
table.classmembers td.decl { text-align:left; padding-left:.5em; white-space: nowrap; }
table.classmembers td.doc { text-align:left; padding-left:.6em; line-height: 1.2em; font-size:80%;}
table.classmembers td.doc div.dox {background-color:#ddd; padding: .1em 1em;}
table.classmembers td.doc div.dox {background-color:#eee; padding: .1em 1em;}
table.classmembers td.doc p { margin: .5em 0; }
table.classmembers td.doc p.para-brief { font-size:120%; }
table.classmembers td.doc p.para-returns { font-size:120%; }
@ -626,7 +631,7 @@ table.classmembers span.em { font-style: italic;}
span.functionname abbr { text-decoration:none; cursor:default;}
div.header {text-align:center;}
div.header h1 {margin:0;}
div.header p {margin:.25em;}
div.header p {margin:.25em; text-align:center;}
</style>
</head>
<body>
@ -664,12 +669,90 @@ Tracks contain specifics. For Example a track <em>has-a</em> diskstream (for fil
</p>
<p>
Operations are performed on objects. One gets a reference to an object and then calls a method.
e.g obj = Session:route_by_name("Audio") obj:set_name("Guitar")
e.g <code>obj = Session:route_by_name("Audio") obj:set_name("Guitar")</code>.
</p>
<p>
Object lifetimes are managed by the Session. Most Objects cannot be directly created, but one asks the Session to create or destroy them. This is mainly due to realtime constrains:
you cannot simply remove a track that is currently processing audio. There are various <em>factory</em> methods for object creation or removal.
</p>
<h2>Pass by Reference</h2>
<p>
Since lua functions are closures, C++ methods that pass arguments by reference cannot be used as-is.
All parameters passed to a C++ method which uses references are returned as Lua Table.
If the C++ method also returns a value it is prefixed. Two parameters are returned: the value and a Lua Table holding the parameters.
</p>
<div class="code">
<div style="float:left;">C++
<pre><code class="cxx">void set_ref (int&amp; var, long&amp; val)
{
printf ("%d %ld\n", var, val);
var = 5;
val = 7;
}
</code></pre>
</div>
<div style="float:right;">Lua
<pre><code class="lua">local var = 0;
ref = set_ref (var, 2);
-- output from C++ printf()
</code><samp class="lua">0 2</samp><code>
-- var is still 0 here
print (ref[1], ref[2])
</code><samp class="lua">5 7</samp></pre>
</div>
</div>
<div class="clear"></div>
<div class="code">
<div style="float:left;">
<pre><code class="cxx">int set_ref2 (int &amp;var, std::string unused)
{
var = 5;
return 3;
}
</code></pre>
</div>
<div style="float:right;">
<pre><code class="lua">rv, ref = set_ref2 (0, "hello");
print (rv, ref[1], ref[2])
</code><samp class="lua">3 5 hello</samp></pre>
</div>
</div>
<div class="clear"></div>
<h2>Pointer Classes</h2>
<p>
Libardour makes extensive use of reference counted <code>boost::shared_ptr</code> to manage lifetimes.
The Lua bindings provide a complete abstration of this. There are no pointers in lua.
For example a <?=typelink('ARDOUR:Route')?> is a pointer in C++, but lua functions operate on it like it was a class instance.
</p>
<p>
<code>shared_ptr</code> are reference counted. Once assigned to a lua variable, the C++ object will be kept and remains valid.
It is good practice to assign references to lua <code>local</code> variables or reset the variable to <code>nil</code> to drop the ref.
</p>
<p>
All pointer classes have a <code>isnil ()</code> method. This is for two cases:
Construction may fail. e.g. <code><?=typelink('ARDOUR:LuaAPI')?>.newplugin()</code>
may not be able to find the given plugin and hence cannot create an object.
</p>
<p>
The second case if for <code>boost::weak_ptr</code>. As opposed to <code>boost::shared_ptr</code> weak-pointers are not reference counted.
The object may vanish at any time.
If lua code calls a method on a nil object, the interpreter will raise an exception and the script will not continue.
This is not unlike <code>a = nil a:test()</code> which results in en error "<em>attempt to index a nil value</em>".
</p>
<p>
From the lua side of things there is no distinction between weak and shared pointers. They behave identically.
Below they're inidicated in orange and have an arrow to indicate the pointer type.
Pointer Classes cannot be created in lua scripts. It always requires a call to C++ to create the Object and obtain a reference to it.
</p>
<?php
echo '<h1 id="h_classes">Class Documentation</h1>'.NL;