STL stream support for Ruby.

docstring and autodoc support for Ruby.




git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@9750 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
Gonzalo Garramuno 2007-05-02 10:00:26 +00:00
commit 594483bbda
12 changed files with 630 additions and 3 deletions

View file

@ -8,13 +8,12 @@ Version 1.3.32 (in progress)
Added typename for raw lua_State*
Added documentation on native functions.
05/07/2007: gga
05/02/2007: gga
[Ruby]
Docstrings are now supported.
%feature("autodoc") and %feature("docstring") are now
properly supported in Ruby. These features will generate
a _wrap.cxx file with rdoc comments in them.
TODO: add doxygen -> rdoc conversion support.
05/05/2007: gga
[Ruby]
@ -40,6 +39,14 @@ Version 1.3.32 (in progress)
exceptions. Now, nil is returned instead, following ruby's
standard Array behavior.
05/02/2007: gga
[Ruby]
Changed the value of SWIG_TYPECHECK_BOOL to be 10000 (ie. higher
than that of all integers). This is because Ruby allows typecasting
integers down to booleans which can make overloaded functions on
bools and integers to fail.
(bug# 1488142)
05/02/2007: gga
[Ruby]
Fixed a subtle bug in multiple argouts that could get triggered if

View file

@ -0,0 +1,59 @@
%module li_std_stream
%inline %{
struct A;
%}
%include <std_iostream.i>
%include <std_sstream.i>
%callback(1) A::bar;
%inline %{
struct B {
virtual ~B()
{
}
};
struct A : B
{
void __add__(int a)
{
}
void __add__(double a)
{
}
static int bar(int a){
return a;
}
static int foo(int a, int (*pf)(int a))
{
return pf(a);
}
std::ostream& __rlshift__(std::ostream& out)
{
out << "A class";
return out;
}
};
%}
%extend std::basic_ostream<char>{
std::basic_ostream<char>&
operator<<(const A& a)
{
*self << "A class";
return *self;
}
}

View file

@ -0,0 +1,22 @@
#!/usr/bin/env ruby
#
# Simple test of std::ostringstream.
#
# Author:: gga
# Copyright:: 2007
# License:: Ruby
#
require 'swig_assert'
require 'li_std_stream'
include Li_std_stream
swig_assert_each_line(<<'EOF', binding)
a = A.new
o = Ostringstream.new
o << a << " " << 2345 << " " << 1.435
o.str == "A class 2345 1.435"
EOF

View file

@ -4,6 +4,9 @@
/* ------------------------------------------------------------
* Fragment section
* ------------------------------------------------------------ */
/* bool is dangerous in Ruby, change precedence */
#undef SWIG_TYPECHECK_BOOL
%define SWIG_TYPECHECK_BOOL 10000 %enddef
/* Include fundamental fragemt definitions */
%include <typemaps/fragments.swg>

View file

@ -0,0 +1,97 @@
#if !defined(SWIG_STD_STRING)
#define SWIG_STD_BASIC_STRING
%include <rubycontainer.swg>
#define %swig_basic_string(Type...) %swig_sequence_methods_val(Type)
%traits_swigtype(std::basic_string<char>);
%fragment(SWIG_Traits_frag(std::basic_string<char>));
%fragment(SWIG_AsPtr_frag(std::basic_string<char>),"header",
fragment="SWIG_AsCharPtrAndSize") {
SWIGINTERN int
SWIG_AsPtr(std::basic_string<char>)(VALUE obj, std::string **val)
{
static swig_type_info* string_info =
SWIG_TypeQuery("std::basic_string<char> *");
std::string *vptr;
if (SWIG_ConvertPtr(obj, (void**)&vptr, string_info, 0) == SWIG_OK) {
if (val) *val = vptr;
return SWIG_OLDOBJ;
} else {
char* buf = 0 ; size_t size = 0; int alloc = 0;
if (SWIG_AsCharPtrAndSize(obj, &buf, &size, &alloc) == SWIG_OK) {
if (buf) {
if (val) *val = new std::string(buf, size - 1);
if (alloc == SWIG_NEWOBJ) %delete_array(buf);
return SWIG_NEWOBJ;
}
}
if (val) {
rb_raise( rb_eTypeError, "a string is expected");
}
return 0;
}
}
}
%fragment(SWIG_From_frag(std::basic_string<char>),"header",
fragment="SWIG_FromCharPtrAndSize") {
SWIGINTERNINLINE VALUE
SWIG_From(std::basic_string<char>)(const std::string& s)
{
return SWIG_FromCharPtrAndSize(s.data(), s.size());
}
}
%include <std/std_basic_string.i>
%typemaps_asptrfromn(%checkcode(STRING), std::basic_string<char>);
#endif
#if !defined(SWIG_STD_WSTRING)
%fragment(SWIG_AsPtr_frag(std::basic_string<wchar_t>),"header",
fragment="SWIG_AsWCharPtrAndSize") {
SWIGINTERN int
SWIG_AsPtr(std::basic_string<wchar_t>)(VALUE obj, std::wstring **val)
{
static swig_type_info* string_info =
SWIG_TypeQuery("std::basic_string<wchar_t> *");
std::wstring *vptr;
if (SWIG_ConvertPtr(obj, (void**)&vptr, string_info, 0) == SWIG_OK) {
if (val) *val = vptr;
return SWIG_OLDOBJ;
} else {
wchar_t *buf = 0 ; size_t size = 0; int alloc = 0;
if (SWIG_AsWCharPtrAndSize(obj, &buf, &size, &alloc) == SWIG_OK) {
if (buf) {
if (val) *val = new std::wstring(buf, size - 1);
if (alloc == SWIG_NEWOBJ) %delete_array(buf);
return SWIG_NEWOBJ;
}
}
if (val) {
rb_raise( rb_eTypeError, "a string is expected");
}
return 0;
}
}
}
%fragment(SWIG_From_frag(std::basic_string<wchar_t>),"header",
fragment="SWIG_FromWCharPtrAndSize") {
SWIGINTERNINLINE VALUE
SWIG_From(std::basic_string<wchar_t>)(const std::wstring& s)
{
return SWIG_FromWCharPtrAndSize(s.data(), s.size());
}
}
%typemaps_asptrfromn(%checkcode(UNISTRING), std::basic_string<wchar_t>);
#endif

View file

@ -0,0 +1 @@
%include <std/std_char_traits.i>

View file

@ -1,3 +1,6 @@
#pragma SWIG nowarn=801
%rename(ios_base_in) std::ios_base::in;
%include <std/std_ios.i>

12
Lib/ruby/std_iostream.i Normal file
View file

@ -0,0 +1,12 @@
namespace std
{
%callback("%s") endl;
%callback("%s") ends;
%callback("%s") flush;
}
%warnfilter(365) operator+=;
%warnfilter(802) std::basic_iostream; // turn off multiple inheritance warning
%include <std/std_iostream.i>

2
Lib/ruby/std_sstream.i Normal file
View file

@ -0,0 +1,2 @@
%include <std/std_sstream.i>

1
Lib/ruby/std_streambuf.i Normal file
View file

@ -0,0 +1 @@
%include <std/std_streambuf.i>

View file

@ -1 +1,6 @@
%warnfilter(801) std::string; // wrong class name
%warnfilter(378) std::basic_string::operator!=;
%include <typemaps/std_string.swg>

View file

@ -113,6 +113,16 @@ public:
};
/* flags for the make_autodoc function */
enum autodoc_t {
AUTODOC_CLASS,
AUTODOC_CTOR,
AUTODOC_DTOR,
AUTODOC_STATICFUNC,
AUTODOC_FUNC,
AUTODOC_METHOD
};
static const char *usage = "\
Ruby Options (available with -ruby)\n\
-globalmodule - Wrap everything into the global module\n\
@ -166,6 +176,378 @@ private:
STATIC_VAR
};
/* ------------------------------------------------------------
* autodoc level declarations
* ------------------------------------------------------------ */
enum autodoc_l {
NO_AUTODOC = -2, // no autodoc
STRING_AUTODOC = -1, // use provided string
NAMES_AUTODOC = 0, // only parameter names
TYPES_AUTODOC = 1, // parameter names and types
EXTEND_AUTODOC = 2, // extended documentation and parameter names
EXTEND_TYPES_AUTODOC = 3 // extended documentation and parameter types + names
};
autodoc_l autodoc_level(String *autodoc) {
autodoc_l dlevel = NO_AUTODOC;
if (autodoc) {
char *c = Char(autodoc);
if (c && isdigit(c[0])) {
dlevel = (autodoc_l) atoi(c);
} else {
if (strcmp(c, "extended") == 0) {
dlevel = EXTEND_AUTODOC;
} else {
dlevel = STRING_AUTODOC;
}
}
}
return dlevel;
}
/* ------------------------------------------------------------
* have_docstring()
* Check if there is a docstring directive and it has text,
* or there is an autodoc flag set
* ------------------------------------------------------------ */
bool have_docstring(Node *n) {
String *str = Getattr(n, "feature:docstring");
return (str != NULL && Len(str) > 0) || (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"));
}
/* ------------------------------------------------------------
* docstring()
* Get the docstring text, stripping off {} if neccessary,
* and enclose in triple double quotes. If autodoc is also
* set then it will build a combined docstring.
* ------------------------------------------------------------ */
String *docstring(Node *n, autodoc_t ad_type) {
String *str = Getattr(n, "feature:docstring");
bool have_ds = (str != NULL && Len(str) > 0);
bool have_auto = (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"));
String *autodoc = NULL;
String *doc = NULL;
if (have_ds) {
char *t = Char(str);
if (*t == '{') {
Delitem(str, 0);
Delitem(str, DOH_END);
}
}
if (have_auto) {
autodoc = make_autodoc(n, ad_type);
have_auto = (autodoc != NULL && Len(autodoc) > 0);
}
// If there is more than one line then make docstrings like this:
//
// This is line1
// And here is line2 followed by the rest of them
//
// otherwise, put it all on a single line
//
if (have_auto && have_ds) { // Both autodoc and docstring are present
doc = NewString("");
Printv(doc, "\n", autodoc, "\n", str, NIL);
} else if (!have_auto && have_ds) { // only docstring
if (Strchr(str, '\n') == NULL) {
doc = NewString(str);
} else {
doc = NewString("");
Printv(doc, str, NIL);
}
} else if (have_auto && !have_ds) { // only autodoc
if (Strchr(autodoc, '\n') == NULL) {
doc = NewStringf("%s", autodoc);
} else {
doc = NewString("");
Printv(doc, "\n", autodoc, NIL);
}
} else
doc = NewString("");
// Save the generated strings in the parse tree in case they are used later
// by post processing tools
Setattr(n, "ruby:docstring", doc);
Setattr(n, "ruby:autodoc", autodoc);
return doc;
}
/* ------------------------------------------------------------
* make_autodocParmList()
* Generate the documentation for the function parameters
* ------------------------------------------------------------ */
String *make_autodocParmList(Node *n, bool showTypes) {
String *doc = NewString("");
String *pdocs = Copy(Getattr(n, "feature:pdocs"));
ParmList *plist = CopyParmList(Getattr(n, "parms"));
Parm *p;
Parm *pnext;
Node *lookup;
int lines = 0;
const int maxwidth = 50;
if (pdocs)
Append(pdocs, "\n");
Swig_typemap_attach_parms("in", plist, 0);
Swig_typemap_attach_parms("doc", plist, 0);
for (p = plist; p; p = pnext) {
String *name = 0;
String *type = 0;
String *value = 0;
String *ptype = 0;
String *pdoc = Getattr(p, "tmap:doc");
if (pdoc) {
name = Getattr(p, "tmap:doc:name");
type = Getattr(p, "tmap:doc:type");
value = Getattr(p, "tmap:doc:value");
ptype = Getattr(p, "tmap:doc:pytype");
}
name = name ? name : Getattr(p, "name");
type = type ? type : Getattr(p, "type");
value = value ? value : Getattr(p, "value");
String *tm = Getattr(p, "tmap:in");
if (tm) {
pnext = Getattr(p, "tmap:in:next");
} else {
pnext = nextSibling(p);
}
// Skip the 'self' parameter which in ruby is implicit
if ( Cmp(name, "self") == 0 )
continue;
if (Len(doc)) {
// add a comma to the previous one if any
Append(doc, ", ");
// Do we need to wrap a long line?
if ((Len(doc) - lines * maxwidth) > maxwidth) {
Printf(doc, "\n%s", tab4);
lines += 1;
}
}
// Do the param type too?
if (showTypes) {
type = SwigType_base(type);
lookup = Swig_symbol_clookup(type, 0);
if (lookup)
type = Getattr(lookup, "sym:name");
Printf(doc, "%s ", type);
}
if (name) {
Append(doc, name);
if (pdoc) {
if (!pdocs)
pdocs = NewString("Parameters:\n");
Printf(pdocs, " %s\n", pdoc);
}
} else {
Append(doc, "?");
}
if (value) {
if (Strcmp(value, "NULL") == 0)
value = NewString("nil");
else if (Strcmp(value, "true") == 0 || Strcmp(value, "TRUE") == 0)
value = NewString("true");
else if (Strcmp(value, "false") == 0 || Strcmp(value, "FALSE") == 0)
value = NewString("false");
else {
lookup = Swig_symbol_clookup(value, 0);
if (lookup)
value = Getattr(lookup, "sym:name");
}
Printf(doc, "=%s", value);
}
}
if (pdocs)
Setattr(n, "feature:pdocs", pdocs);
Delete(plist);
return doc;
}
/* ------------------------------------------------------------
* make_autodoc()
* Build a docstring for the node, using parameter and other
* info in the parse tree. If the value of the autodoc
* attribute is "0" then do not include parameter types, if
* it is "1" (the default) then do. If it has some other
* value then assume it is supplied by the extension writer
* and use it directly.
* ------------------------------------------------------------ */
String *make_autodoc(Node *n, autodoc_t ad_type) {
int extended = 0;
// If the function is overloaded then this funciton is called
// for the last one. Rewind to the first so the docstrings are
// in order.
while (Getattr(n, "sym:previousSibling"))
n = Getattr(n, "sym:previousSibling");
Node *pn = Swig_methodclass(n);
String* class_name = Getattr(pn, "sym:name");
String* full_name;
if ( module ) {
full_name = NewString(module);
Append(full_name, "::");
}
else
full_name = NewString("");
if ( class_name )
Append(full_name, class_name);
String *doc = NewString("/*\n");
int counter = 0;
for ( ; n; ++counter ) {
bool showTypes = false;
bool skipAuto = false;
String *autodoc = Getattr(n, "feature:autodoc");
autodoc_l dlevel = autodoc_level(autodoc);
switch (dlevel) {
case NO_AUTODOC:
break;
case NAMES_AUTODOC:
showTypes = false;
break;
case TYPES_AUTODOC:
showTypes = true;
break;
case EXTEND_AUTODOC:
extended = 1;
showTypes = false;
break;
case EXTEND_TYPES_AUTODOC:
extended = 1;
showTypes = true;
break;
case STRING_AUTODOC:
Append(doc, autodoc);
skipAuto = true;
break;
}
if (!skipAuto) {
String *symname = Getattr(n, "sym:name");
if ( Getattr( special_methods, symname ) )
symname = Getattr( special_methods, symname );
SwigType *type = Getattr(n, "type");
if (type) {
if (Strcmp(type, "void") == 0)
type = NULL;
else {
SwigType *qt = SwigType_typedef_resolve_all(type);
if (SwigType_isenum(qt))
type = NewString("int");
else {
type = SwigType_base(type);
Node *lookup = Swig_symbol_clookup(type, 0);
if (lookup)
type = Getattr(lookup, "sym:name");
}
}
}
switch (ad_type) {
case AUTODOC_CLASS:
{
// Only do the autodoc if there isn't a docstring for the class
String *str = Getattr(n, "feature:docstring");
if (counter == 0 && (str == NULL || Len(str) == 0)) {
if (CPlusPlus) {
Printf(doc, " Document-class: %s\n\n Proxy of C++ %s class",
full_name, class_name);
} else {
Printf(doc, " Document-class: %s\n\n Proxy of C %s struct",
full_name, class_name);
}
}
}
break;
case AUTODOC_CTOR:
if (counter == 0)
Append(doc, " Document-method: new\n\n call-seq:\n");
if (Strcmp(class_name, symname) == 0) {
String *paramList = make_autodocParmList(n, showTypes);
if (Len(paramList))
Printf(doc, " %s.new(%s)", class_name, paramList);
else
Printf(doc, " %s.new", class_name);
} else
Printf(doc, " %s.new(%s)", class_name,
make_autodocParmList(n, showTypes));
break;
case AUTODOC_DTOR:
break;
case AUTODOC_STATICFUNC:
if (counter == 0)
Printf(doc, " Document-method: %s\n\n call-seq:\n", full_name);
Printf(doc, " %s(%s)", full_name,
make_autodocParmList(n, showTypes));
if (type)
Printf(doc, " -> %s", type);
break;
case AUTODOC_FUNC:
if (counter == 0)
Printf(doc, " Document-method: %s\n\n call-seq:\n", symname);
Printf(doc, " %s(%s)", symname,
make_autodocParmList(n, showTypes));
if (type)
Printf(doc, " -> %s", type);
break;
case AUTODOC_METHOD:
if (counter == 0)
Printf(doc, " Document-method: %s\n\n call-seq:\n", symname);
String *paramList = make_autodocParmList(n, showTypes);
if (Len(paramList))
Printf(doc, " %s(%s)", symname, paramList);
else
Printf(doc, " %s", symname);
if (type)
Printf(doc, " -> %s", type);
break;
}
}
if (extended) {
String *pdocs = Getattr(n, "feature:pdocs");
if (pdocs) {
Printv(doc, "\n", pdocs, NULL);
}
}
// if it's overloaded then get the next decl and loop around again
n = Getattr(n, "sym:nextSibling");
if (n)
Append(doc, "\n");
}
Append(doc, "\n\n*/\n");
Delete(full_name);
return doc;
}
public:
/* ---------------------------------------------------------------------
@ -1064,6 +1446,9 @@ public:
bool ctor_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n));
int start = (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) ? 1 : 0;
/* Now write the wrapper function itself */
if (current == CONSTRUCTOR_ALLOCATE) {
Printf(f->def, "#ifdef HAVE_RB_DEFINE_ALLOC_FUNC\n");
@ -1110,7 +1495,7 @@ public:
insertArgOutputCode(l, outarg, need_result);
/* if the object is a director, and the method call originated from its
* underlying python object, resolve the call by going up the c++
* underlying Ruby object, resolve the call by going up the c++
* inheritance chain. otherwise try to resolve the method in python.
* without this check an infinite loop is set up between the director and
* shadow class method calls.
@ -1751,6 +2136,9 @@ public:
* ---------------------------------------------------------------------- */
virtual int classHandler(Node *n) {
String* docs = docstring(n, AUTODOC_CLASS);
Printf(f_wrappers, "%s", docs);
Delete(docs);
String *name = Getattr(n, "name");
String *symname = Getattr(n, "sym:name");
@ -1842,6 +2230,11 @@ public:
virtual int memberfunctionHandler(Node *n) {
current = MEMBER_FUNC;
String* docs = docstring(n, AUTODOC_METHOD);
Printf(f_wrappers, "%s", docs);
Delete(docs);
Language::memberfunctionHandler(n);
current = NO_CPP;
return SWIG_OK;
@ -1908,7 +2301,13 @@ public:
Delete(self);
}
/* Now do the instance initialize method */
String* docs = docstring(n, AUTODOC_CTOR);
Printf(f_wrappers, "%s", docs);
Delete(docs);
current = CONSTRUCTOR_INITIALIZE;
Swig_name_register((String_or_char *) "construct", (String_or_char *) "new_%c");
Language::constructorHandler(n);
@ -2005,6 +2404,10 @@ public:
* -------------------------------------------------------------------- */
virtual int membervariableHandler(Node *n) {
String* docs = docstring(n, AUTODOC_METHOD);
Printf(f_wrappers, "%s", docs);
Delete(docs);
current = MEMBER_VAR;
Language::membervariableHandler(n);
current = NO_CPP;
@ -2018,6 +2421,10 @@ public:
* ---------------------------------------------------------------------- */
virtual int staticmemberfunctionHandler(Node *n) {
String* docs = docstring(n, AUTODOC_STATICFUNC);
Printf(f_wrappers, "%s", docs);
Delete(docs);
current = STATIC_FUNC;
Language::staticmemberfunctionHandler(n);
current = NO_CPP;
@ -2031,6 +2438,10 @@ public:
* --------------------------------------------------------------------- */
virtual int memberconstantHandler(Node *n) {
String* docs = docstring(n, AUTODOC_STATICFUNC);
Printf(f_wrappers, "%s", docs);
Delete(docs);
current = CLASS_CONST;
Language::memberconstantHandler(n);
current = NO_CPP;
@ -2042,6 +2453,10 @@ public:
* --------------------------------------------------------------------- */
virtual int staticmembervariableHandler(Node *n) {
String* docs = docstring(n, AUTODOC_STATICFUNC);
Printf(f_wrappers, "%s", docs);
Delete(docs);
current = STATIC_VAR;
Language::staticmembervariableHandler(n);
current = NO_CPP;