Rework std::initializer_list handling to warn about usage in any method, not just constructors. A typemap is used to issue the warning and can be overridden with user defined behaviour.

This commit is contained in:
William S Fulton 2013-02-04 07:12:05 +00:00
commit d613ef42f2
10 changed files with 259 additions and 134 deletions

View file

@ -125,53 +125,133 @@ public:
<H3><a name="Cpp0x_Initializer_lists"></a>7.2.4 Initializer lists</H3>
<p>
Constructors using the std::initializer_list class are removed
from the wrapped class because the only way to access such a
constructor is at compile time using the initialization list syntax.
Initializer lists are very much a C++ construct and not very accessible from wrappers.
Initializer lists are very much a C++ compiler construct and are not very accessible from wrappers as
they are intended for compile time initialization of classes using the special <tt>std::initializer_list</tt> type.
SWIG detects usage of initializer lists and will emit a special informative warning each time one is used:
</p>
<p>For now, if you want to fill the class components like this:</p>
<div class="shell">
<pre>
example.i:33: Warning 476: Initialization using std::initializer_list.
</pre>
</div>
<p>
Initializer lists usually appear in constructors but can appear in any function or method.
They often appear in constructors which are overloaded with alternative approaches to initializing a class,
such as the std container's push_back method for adding elements to a container.
The recommended approach then is to simply ignore the initializer-list constructor, for example:
</p>
<div class="code"><pre>
class A {
%ignore Container::Container(std::initializer_list&lt;int&gt;);
class Container {
public:
A( std::initializer_list&lt;int&gt; );
Container(std::initializer_list&lt;int&gt;); // initializer-list constructor
Container();
void push_back(const int &amp;);
...
};
A a1 = {1,2,3,4};
</pre></div>
<p>you could add another constructor using <tt>std::vector</tt> for example:</p>
<p>Alternatively you could modify the class and add another constructor for initialization by some other means,
for example by a <tt>std::vector</tt>:</p>
<div class="code"><pre>
class A {
%include &lt;std_vector.i&gt;
class Container {
public:
A( std::initializer_list&lt;int&gt; );
A( std::vector&lt;int&gt; );
Container(const std::vector&lt;int&gt; &amp;);
Container(std::initializer_list&lt;int&gt;); // initializer-list constructor
Container();
void push_back(const int &amp;);
...
};
A a1 = {1,2,3,4};
</pre></div>
<p>And then construct it from your target language, for example, in Python:</p>
<p>And then call this constructor from your target language, for example, in Python, the following will call the constructor taking the <tt>std::vector</tt>:</p>
<div class="targetlang"><pre>
&gt;&gt;&gt; a2 = A( [1,2,3,4] )
&gt;&gt;&gt; c = Container( [1,2,3,4] )
</pre></div>
<p>
<tt>std::initializer_list</tt> is simply a container that can only be initialised at compile time.
As such it is possible to write typemaps for a target language container to map onto
<tt>std::initializer_list</tt>. However, this can only be done for a fixed number of elements ...
there is no way to construct an initializer list with a variable number of arguments at runtime.
This is not particularly flexible though outside of C++ static initialization,
hence the need to provide an alternative for use from a target language.
If you are unable to modify the class being wrapped, consider ignoring the initializer-list constructor and using
%extend to add in an alternative constructor:
</p>
<div class="code"><pre>
%include &lt;std_vector.i&gt;
%extend Container {
Container(const std::vector&lt;int&gt; &amp;elements) {
Container *c = new Container();
for (int element : elements)
c-&gt;push_back(element);
return c;
}
}
%ignore Container::Container(std::initializer_list&lt;int&gt;);
class Container {
public:
Container(std::initializer_list&lt;int&gt;); // initializer-list constructor
Container();
void push_back(const int &amp;);
...
};
</pre></div>
<p>
The above makes the wrappers look is as if the class had been declared as follows:
</p>
<div class="code"><pre>
%include &lt;std_vector.i&gt;
class Container {
public:
Container(const std::vector&lt;int&gt; &amp;);
// Container(std::initializer_list&lt;int&gt;); // initializer-list constructor (ignored)
Container();
void push_back(const int &amp;);
...
};
</pre></div>
<p>
<tt>std::initializer_list</tt> is simply a container that can only be initialized at compile time.
As it is just a C++ type, it is possible to write typemaps for a target language container to map onto
<tt>std::initializer_list</tt>. However, this can only be done for a fixed number of elements as
initializer lists are not designed to be constructed with a variable number of arguments at runtime.
The example below is a very simple approach which ignores any parameters passed in and merely initializes
with a fixed list of fixed integer values chosen at compile time:
</p>
<div class="code"><pre>
%typemap(in) std::initializer_list&lt;int&gt; {
$1 = {10, 20, 30, 40, 50};
}
class Container {
public:
Container(std::initializer_list&lt;int&gt;); // initializer-list constructor
Container();
void push_back(const int &amp;);
...
};
</pre></div>
<p>
Any attempt at passing in values from the target language will be ignored and replaced by <tt>{10, 20, 30, 40, 50}</tt>.
Needless to say, this approach is very limited, but could be improved upon, but only slightly.
A typemap could be written to map a fixed number of elements on to the <tt>std::initializer_list</tt>,
but with values decided at runtime.
The typemaps would be target language specific.
</p>
<p>
Initializer lists can appear in any function or method, not just constructors.
SWIG only ignores the constructors as this is where they commonly occur.
Users are recommended to manually ignore any other methods using an initialization list with <tt>%ignore</tt>.
Note that the default typemap for <tt>std::initializer_list</tt> does nothing but issue the warning
and hence any user supplied typemaps will override it and suppress the warning.
</p>
<H3><a name="Cpp0x_Uniform_initialization"></a>7.2.5 Uniform initialization</H3>

View file

@ -497,6 +497,7 @@ example.i(4) : Syntax error in input.
<li>471. Unable to use return type <em>type</em> in director method
<li>474. Method <em>method</em> usage of the optimal attribute ignored in the out typemap as the following cannot be used to generate optimal code: <em>code</em>
<li>475. Multiple calls to <em>method</em> might be generated due to optimal attribute usage in the out typemap.
<li>476. Initialization using std::initializer_list.
</ul>

View file

@ -486,6 +486,7 @@ CPP0X_TEST_CASES = \
cpp0x_explicit_conversion_operators \
cpp0x_function_objects \
cpp0x_initializer_list \
cpp0x_initializer_list_extend \
cpp0x_lambda_functions \
cpp0x_null_pointer_constant \
cpp0x_raw_string_literals \

View file

@ -1,22 +1,34 @@
/* This testcase checks whether SWIG correctly uses the new initializer_list
/* This testcase shows a few simple ways to deal with the new initializer_list
introduced in C++0x. */
%module cpp0x_initializer_list
%warnfilter(SWIGWARN_LANG_INITIALIZER_LIST) A;
%warnfilter(SWIGWARN_TYPEMAP_INITIALIZER_LIST) B::B;
%ignore A::A(std::initializer_list<int>);
%ignore B::method;
%typemap(in) std::initializer_list<const char *> {
$1 = {"Ab", "Fab"};
}
%inline %{
#include <initializer_list>
class A {
public:
A( std::initializer_list<int> ) {}
A(std::initializer_list<int>) {}
A() {}
A(double d) {}
};
class B {
public:
B( std::initializer_list<int>, std::initializer_list<double> ) {}
B(std::initializer_list<int>, std::initializer_list<double>) {}
B() {}
void method(std::initializer_list<int> init) {}
};
class C {
public:
C(std::initializer_list<const char *>) {}
C() {}
};
%}

View file

@ -0,0 +1,29 @@
/* This testcase shows how to replace std_initializer_list with std_vector. */
%module cpp0x_initializer_list_extend
%ignore Container::Container(std::initializer_list<int>);
%include <std_vector.i>
%template(VectorInt) std::vector<int>;
%extend Container {
Container(const std::vector<int> &elements) {
Container *c = new Container();
for (int element : elements)
c->push_back(element);
return c;
}
}
%inline %{
#include <initializer_list>
class Container {
public:
Container(std::initializer_list<int>) {}
Container() {}
void push_back(const int&) {}
};
%}

View file

@ -0,0 +1,4 @@
import cpp0x_initializer_list_extend
c = cpp0x_initializer_list_extend.Container( [10, 20, 30, 40] )

View file

@ -309,6 +309,88 @@ static int NAME(TYPE x) {
%include <swigwarnings.swg>
/* -----------------------------------------------------------------------------
* Overloading support
* ----------------------------------------------------------------------------- */
/*
* Function/method overloading support. This is done through typemaps,
* but also involve a precedence level.
*/
/* Macro for overload resolution */
%define %typecheck(_x...) %typemap(typecheck, precedence=_x) %enddef
/* Macros for precedence levels */
%define SWIG_TYPECHECK_POINTER 0 %enddef
%define SWIG_TYPECHECK_ITERATOR 5 %enddef
%define SWIG_TYPECHECK_VOIDPTR 10 %enddef
%define SWIG_TYPECHECK_BOOL 15 %enddef
%define SWIG_TYPECHECK_UINT8 20 %enddef
%define SWIG_TYPECHECK_INT8 25 %enddef
%define SWIG_TYPECHECK_UINT16 30 %enddef
%define SWIG_TYPECHECK_INT16 35 %enddef
%define SWIG_TYPECHECK_UINT32 40 %enddef
%define SWIG_TYPECHECK_INT32 45 %enddef
%define SWIG_TYPECHECK_SIZE 47 %enddef
%define SWIG_TYPECHECK_PTRDIFF 48 %enddef
%define SWIG_TYPECHECK_UINT64 50 %enddef
%define SWIG_TYPECHECK_INT64 55 %enddef
%define SWIG_TYPECHECK_UINT128 60 %enddef
%define SWIG_TYPECHECK_INT128 65 %enddef
%define SWIG_TYPECHECK_INTEGER 70 %enddef
%define SWIG_TYPECHECK_FLOAT 80 %enddef
%define SWIG_TYPECHECK_DOUBLE 90 %enddef
%define SWIG_TYPECHECK_CPLXFLT 95 %enddef
%define SWIG_TYPECHECK_CPLXDBL 100 %enddef
%define SWIG_TYPECHECK_COMPLEX 105 %enddef
%define SWIG_TYPECHECK_UNICHAR 110 %enddef
%define SWIG_TYPECHECK_STDUNISTRING 115 %enddef
%define SWIG_TYPECHECK_UNISTRING 120 %enddef
%define SWIG_TYPECHECK_CHAR 130 %enddef
%define SWIG_TYPECHECK_STDSTRING 135 %enddef
%define SWIG_TYPECHECK_STRING 140 %enddef
%define SWIG_TYPECHECK_PAIR 150 %enddef
%define SWIG_TYPECHECK_VECTOR 160 %enddef
%define SWIG_TYPECHECK_DEQUE 170 %enddef
%define SWIG_TYPECHECK_LIST 180 %enddef
%define SWIG_TYPECHECK_SET 190 %enddef
%define SWIG_TYPECHECK_MULTISET 200 %enddef
%define SWIG_TYPECHECK_MAP 210 %enddef
%define SWIG_TYPECHECK_MULTIMAP 220 %enddef
%define SWIG_TYPECHECK_STACK 230 %enddef
%define SWIG_TYPECHECK_QUEUE 240 %enddef
%define SWIG_TYPECHECK_BOOL_ARRAY 1015 %enddef
%define SWIG_TYPECHECK_INT8_ARRAY 1025 %enddef
%define SWIG_TYPECHECK_INT16_ARRAY 1035 %enddef
%define SWIG_TYPECHECK_INT32_ARRAY 1045 %enddef
%define SWIG_TYPECHECK_INT64_ARRAY 1055 %enddef
%define SWIG_TYPECHECK_INT128_ARRAY 1065 %enddef
%define SWIG_TYPECHECK_FLOAT_ARRAY 1080 %enddef
%define SWIG_TYPECHECK_DOUBLE_ARRAY 1090 %enddef
%define SWIG_TYPECHECK_CHAR_ARRAY 1130 %enddef
%define SWIG_TYPECHECK_STRING_ARRAY 1140 %enddef
%define SWIG_TYPECHECK_OBJECT_ARRAY 1150 %enddef
%define SWIG_TYPECHECK_BOOL_PTR 2015 %enddef
%define SWIG_TYPECHECK_UINT8_PTR 2020 %enddef
%define SWIG_TYPECHECK_INT8_PTR 2025 %enddef
%define SWIG_TYPECHECK_UINT16_PTR 2030 %enddef
%define SWIG_TYPECHECK_INT16_PTR 2035 %enddef
%define SWIG_TYPECHECK_UINT32_PTR 2040 %enddef
%define SWIG_TYPECHECK_INT32_PTR 2045 %enddef
%define SWIG_TYPECHECK_UINT64_PTR 2050 %enddef
%define SWIG_TYPECHECK_INT64_PTR 2055 %enddef
%define SWIG_TYPECHECK_FLOAT_PTR 2080 %enddef
%define SWIG_TYPECHECK_DOUBLE_PTR 2090 %enddef
%define SWIG_TYPECHECK_CHAR_PTR 2130 %enddef
%define SWIG_TYPECHECK_SWIGOBJECT 5000 %enddef
/* -----------------------------------------------------------------------------
* Default handling of certain overloaded operators
* ----------------------------------------------------------------------------- */
@ -340,6 +422,10 @@ static int NAME(TYPE x) {
/* Define std namespace */
namespace std {
/* Warn about std::initializer_list usage. The constructor/method where used should probably be ignored. See docs. */
template<typename T> class initializer_list {};
%typemap(in, warning=SWIGWARN_TYPEMAP_INITIALIZER_LIST_MSG) initializer_list<T> ""
%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) initializer_list<T> ""
}
#endif
@ -500,89 +586,6 @@ namespace std {
}
}
/* -----------------------------------------------------------------------------
* Overloading support
* ----------------------------------------------------------------------------- */
/*
* Function/method overloading support. This is done through typemaps,
* but also involve a precedence level.
*/
/* Macro for overload resolution */
%define %typecheck(_x...) %typemap(typecheck, precedence=_x) %enddef
/* Macros for precedence levels */
%define SWIG_TYPECHECK_POINTER 0 %enddef
%define SWIG_TYPECHECK_ITERATOR 5 %enddef
%define SWIG_TYPECHECK_VOIDPTR 10 %enddef
%define SWIG_TYPECHECK_BOOL 15 %enddef
%define SWIG_TYPECHECK_UINT8 20 %enddef
%define SWIG_TYPECHECK_INT8 25 %enddef
%define SWIG_TYPECHECK_UINT16 30 %enddef
%define SWIG_TYPECHECK_INT16 35 %enddef
%define SWIG_TYPECHECK_UINT32 40 %enddef
%define SWIG_TYPECHECK_INT32 45 %enddef
%define SWIG_TYPECHECK_SIZE 47 %enddef
%define SWIG_TYPECHECK_PTRDIFF 48 %enddef
%define SWIG_TYPECHECK_UINT64 50 %enddef
%define SWIG_TYPECHECK_INT64 55 %enddef
%define SWIG_TYPECHECK_UINT128 60 %enddef
%define SWIG_TYPECHECK_INT128 65 %enddef
%define SWIG_TYPECHECK_INTEGER 70 %enddef
%define SWIG_TYPECHECK_FLOAT 80 %enddef
%define SWIG_TYPECHECK_DOUBLE 90 %enddef
%define SWIG_TYPECHECK_CPLXFLT 95 %enddef
%define SWIG_TYPECHECK_CPLXDBL 100 %enddef
%define SWIG_TYPECHECK_COMPLEX 105 %enddef
%define SWIG_TYPECHECK_UNICHAR 110 %enddef
%define SWIG_TYPECHECK_STDUNISTRING 115 %enddef
%define SWIG_TYPECHECK_UNISTRING 120 %enddef
%define SWIG_TYPECHECK_CHAR 130 %enddef
%define SWIG_TYPECHECK_STDSTRING 135 %enddef
%define SWIG_TYPECHECK_STRING 140 %enddef
%define SWIG_TYPECHECK_PAIR 150 %enddef
%define SWIG_TYPECHECK_VECTOR 160 %enddef
%define SWIG_TYPECHECK_DEQUE 170 %enddef
%define SWIG_TYPECHECK_LIST 180 %enddef
%define SWIG_TYPECHECK_SET 190 %enddef
%define SWIG_TYPECHECK_MULTISET 200 %enddef
%define SWIG_TYPECHECK_MAP 210 %enddef
%define SWIG_TYPECHECK_MULTIMAP 220 %enddef
%define SWIG_TYPECHECK_STACK 230 %enddef
%define SWIG_TYPECHECK_QUEUE 240 %enddef
%define SWIG_TYPECHECK_BOOL_ARRAY 1015 %enddef
%define SWIG_TYPECHECK_INT8_ARRAY 1025 %enddef
%define SWIG_TYPECHECK_INT16_ARRAY 1035 %enddef
%define SWIG_TYPECHECK_INT32_ARRAY 1045 %enddef
%define SWIG_TYPECHECK_INT64_ARRAY 1055 %enddef
%define SWIG_TYPECHECK_INT128_ARRAY 1065 %enddef
%define SWIG_TYPECHECK_FLOAT_ARRAY 1080 %enddef
%define SWIG_TYPECHECK_DOUBLE_ARRAY 1090 %enddef
%define SWIG_TYPECHECK_CHAR_ARRAY 1130 %enddef
%define SWIG_TYPECHECK_STRING_ARRAY 1140 %enddef
%define SWIG_TYPECHECK_OBJECT_ARRAY 1150 %enddef
%define SWIG_TYPECHECK_BOOL_PTR 2015 %enddef
%define SWIG_TYPECHECK_UINT8_PTR 2020 %enddef
%define SWIG_TYPECHECK_INT8_PTR 2025 %enddef
%define SWIG_TYPECHECK_UINT16_PTR 2030 %enddef
%define SWIG_TYPECHECK_INT16_PTR 2035 %enddef
%define SWIG_TYPECHECK_UINT32_PTR 2040 %enddef
%define SWIG_TYPECHECK_INT32_PTR 2045 %enddef
%define SWIG_TYPECHECK_UINT64_PTR 2050 %enddef
%define SWIG_TYPECHECK_INT64_PTR 2055 %enddef
%define SWIG_TYPECHECK_FLOAT_PTR 2080 %enddef
%define SWIG_TYPECHECK_DOUBLE_PTR 2090 %enddef
%define SWIG_TYPECHECK_CHAR_PTR 2130 %enddef
%define SWIG_TYPECHECK_SWIGOBJECT 5000 %enddef
/* -----------------------------------------------------------------------------
* Runtime code
* ----------------------------------------------------------------------------- */

View file

@ -54,6 +54,7 @@
%define SWIGWARN_TYPEMAP_SWIGTYPELEAK_MSG "454:Setting a pointer/reference variable may leak memory." %enddef
%define SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG "470:Thread/reentrant unsafe wrapping, consider returning by value instead." %enddef
%define SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG "473:Returning a pointer or reference in a director method is not recommended." %enddef
%define SWIGWARN_TYPEMAP_INITIALIZER_LIST_MSG "476:Initialization using std::initializer_list." %enddef
/* -----------------------------------------------------------------------------
* Operator related warning messages

View file

@ -4635,27 +4635,21 @@ cpp_member : c_declaration { $$ = $1; }
cpp_constructor_decl : storage_class type LPAREN parms RPAREN ctor_end {
if (Classprefix) {
if ($4 && Getattr($4,"type") && strstr(Char(Getattr($4,"type")), "initializer_list<")) {
/* Ignore constructors containing initializer_list<> introduced in C++0x */
Swig_warning(WARN_LANG_INITIALIZER_LIST, cparse_file, cparse_line, "Constructor with std::initializer_list<> argument ignored.\n");
$$ = 0;
} else {
SwigType *decl = NewStringEmpty();
$$ = new_node("constructor");
Setattr($$,"storage",$1);
Setattr($$,"name",$2);
Setattr($$,"parms",$4);
SwigType_add_function(decl,$4);
Setattr($$,"decl",decl);
Setattr($$,"throws",$6.throws);
Setattr($$,"throw",$6.throwf);
if (Len(scanner_ccode)) {
String *code = Copy(scanner_ccode);
Setattr($$,"code",code);
Delete(code);
}
SetFlag($$,"feature:new");
}
SwigType *decl = NewStringEmpty();
$$ = new_node("constructor");
Setattr($$,"storage",$1);
Setattr($$,"name",$2);
Setattr($$,"parms",$4);
SwigType_add_function(decl,$4);
Setattr($$,"decl",decl);
Setattr($$,"throws",$6.throws);
Setattr($$,"throw",$6.throwf);
if (Len(scanner_ccode)) {
String *code = Copy(scanner_ccode);
Setattr($$,"code",code);
Delete(code);
}
SetFlag($$,"feature:new");
} else {
$$ = 0;
}

View file

@ -174,6 +174,7 @@
#define WARN_TYPEMAP_DIRECTOROUT_PTR 473
#define WARN_TYPEMAP_OUT_OPTIMAL_IGNORED 474
#define WARN_TYPEMAP_OUT_OPTIMAL_MULTIPLE 475
#define WARN_TYPEMAP_INITIALIZER_LIST 476
/* -- Fragments -- */
#define WARN_FRAGMENT_NOT_FOUND 490
@ -201,7 +202,6 @@
#define WARN_LANG_TEMPLATE_METHOD_IGNORE 519
#define WARN_LANG_SMARTPTR_MISSING 520
#define WARN_LANG_ILLEGAL_DESTRUCTOR 521
#define WARN_LANG_INITIALIZER_LIST 522
/* -- Reserved (600-799) -- */