interrogate: support MAKE_MAP_PROPERTY

This commit is contained in:
rdb 2017-10-08 23:56:25 +02:00
parent 0e2b14cf4c
commit bf190f7306
27 changed files with 5650 additions and 4835 deletions

File diff suppressed because it is too large Load Diff

View File

@ -397,6 +397,7 @@ pop_struct() {
%type <u.type> class_derivation_name %type <u.type> class_derivation_name
%type <u.type> enum_element_type %type <u.type> enum_element_type
%type <u.type> maybe_trailing_return_type %type <u.type> maybe_trailing_return_type
%type <u.identifier> maybe_comma_identifier
/*%type <u.type> typedefname*/ /*%type <u.type> typedefname*/
%type <u.identifier> name %type <u.identifier> name
%type <u.identifier> name_no_final %type <u.identifier> name_no_final
@ -548,202 +549,264 @@ declaration:
{ {
current_scope->set_current_vis(V_private); current_scope->set_current_vis(V_private);
} }
| KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ')' ';' | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER maybe_comma_identifier ')' ';'
{
CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
}
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), NULL, current_scope, @1.file);
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
| KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{ {
CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5); yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
} else { } else {
CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer); CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file);
CPPFunctionGroup *setter_func = NULL; make_property->_get_function = getter->as_function_group();
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { if ($6 != nullptr) {
yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7); CPPDeclaration *setter = $6->find_symbol(current_scope, global_scope, current_lexer);
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $6->get_fully_scoped_name(), @6);
} else { } else {
setter_func = setter->as_function_group(); make_property->_set_function = setter->as_function_group();
}
} }
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
setter_func, current_scope, @1.file);
current_scope->add_declaration(make_property, global_scope, current_lexer, @1); current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
} }
} }
| KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';' | KW_MAKE_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{ {
CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5); yyerror("Reference to non-existent or invalid getter: " + $5->get_fully_scoped_name(), @5);
} else { } else {
CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer); CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal, current_scope, @1.file);
CPPFunctionGroup *setter_func = NULL; make_property->_get_function = getter->as_function_group();
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7); yyerror("Reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7);
} else { } else {
setter_func = setter->as_function_group(); make_property->_set_function = setter->as_function_group();
} }
CPPDeclaration *deleter = $9->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *deleter = $9->find_symbol(current_scope, global_scope, current_lexer);
if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) { if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid delete method: " + $9->get_fully_scoped_name(), @9); yyerror("reference to non-existent or invalid delete method: " + $9->get_fully_scoped_name(), @9);
deleter = NULL; } else {
}
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
setter_func, current_scope, @1.file);
if (deleter) {
make_property->_del_function = deleter->as_function_group(); make_property->_del_function = deleter->as_function_group();
} }
current_scope->add_declaration(make_property, global_scope, current_lexer, @1); current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
} }
} }
| KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';' | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{ {
CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5); yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
length_getter = NULL; length_getter = nullptr;
} }
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
getter = nullptr;
} }
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), NULL, current_scope, @1.file); if (getter != nullptr && length_getter != nullptr) {
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
make_property->_length_function = length_getter->as_function_group(); make_property->_length_function = length_getter->as_function_group();
current_scope->add_declaration(make_property, global_scope, current_lexer, @1); current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
} }
| KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';' | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{ {
CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5); yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
length_getter = NULL; length_getter = nullptr;
} }
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
getter = nullptr;
} else {
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
CPPFunctionGroup *setter_func = NULL;
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
} else {
setter_func = setter->as_function_group();
} }
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(), if (getter != nullptr && length_getter != nullptr) {
setter_func, current_scope, @1.file); CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
make_property->_length_function = length_getter->as_function_group(); make_property->_length_function = length_getter->as_function_group();
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
} else {
make_property->_set_function = setter->as_function_group();
}
current_scope->add_declaration(make_property, global_scope, current_lexer, @1); current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
} }
} }
| KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';' | KW_MAKE_SEQ_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{ {
CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *length_getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (length_getter == (CPPDeclaration *)NULL || length_getter->get_subtype() != CPPDeclaration::ST_function_group) { if (length_getter == nullptr || length_getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5); yyerror("reference to non-existent or invalid length method: " + $5->get_fully_scoped_name(), @5);
length_getter = NULL; length_getter = NULL;
} }
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
getter = nullptr;
}
if (getter != nullptr && length_getter != nullptr) {
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_sequence, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
make_property->_length_function = length_getter->as_function_group();
} else {
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
CPPFunctionGroup *setter_func = NULL; if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9); yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
} else { } else {
setter_func = setter->as_function_group(); make_property->_set_function = setter->as_function_group();
} }
CPPDeclaration *deleter = $11->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *deleter = $11->find_symbol(current_scope, global_scope, current_lexer);
if (deleter == (CPPDeclaration *)NULL || deleter->get_subtype() != CPPDeclaration::ST_function_group) { if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid delete method: " + $11->get_fully_scoped_name(), @11); yyerror("reference to non-existent or invalid delete method: " + $11->get_fully_scoped_name(), @11);
deleter = NULL; } else {
}
CPPMakeProperty *make_property = new CPPMakeProperty($3, getter->as_function_group(),
setter_func, current_scope, @1.file);
make_property->_length_function = length_getter->as_function_group();
if (deleter) {
make_property->_del_function = deleter->as_function_group(); make_property->_del_function = deleter->as_function_group();
} }
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ')' ';'
{
CPPDeclaration *getter = $5->find_symbol(current_scope, global_scope, current_lexer);
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid item getter method: " + $5->get_fully_scoped_name(), @5);
} else {
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
} else {
CPPMakeProperty *make_property;
make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
} else {
make_property->_has_function = hasser->as_function_group();
}
current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
}
}
| KW_MAKE_MAP_PROPERTY '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER maybe_comma_identifier ')' ';'
{
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
} else {
CPPMakeProperty *make_property = new CPPMakeProperty($3, CPPMakeProperty::T_mapping, current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
} else {
make_property->_has_function = hasser->as_function_group();
}
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer);
if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9);
} else {
make_property->_set_function = setter->as_function_group();
}
if ($10 != nullptr) {
CPPDeclaration *deleter = $10->find_symbol(current_scope, global_scope, current_lexer);
if (deleter == nullptr || deleter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid delete method: " + $10->get_fully_scoped_name(), @10);
} else {
make_property->_del_function = deleter->as_function_group();
}
}
current_scope->add_declaration(make_property, global_scope, current_lexer, @1); current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
} }
} }
| KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';' | KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{ {
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid has-function: " + $5->get_fully_scoped_name(), @5);
}
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
} else {
CPPMakeProperty *make_property;
make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal,
current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
} else {
make_property->_has_function = hasser->as_function_group();
} }
if (hasser && getter) {
CPPMakeProperty *make_property;
make_property = new CPPMakeProperty($3,
hasser->as_function_group(),
getter->as_function_group(),
NULL, NULL,
current_scope, @1.file);
current_scope->add_declaration(make_property, global_scope, current_lexer, @1); current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
} }
} }
| KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';' | KW_MAKE_PROPERTY2 '(' name ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ',' IDENTIFIER ')' ';'
{ {
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == (CPPDeclaration *)NULL || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid has-function: " + $5->get_fully_scoped_name(), @5);
}
CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *getter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (getter == (CPPDeclaration *)NULL || getter->get_subtype() != CPPDeclaration::ST_function_group) { if (getter == nullptr || getter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7); yyerror("Reference to non-existent or invalid getter: " + $7->get_fully_scoped_name(), @7);
} else {
CPPMakeProperty *make_property;
make_property = new CPPMakeProperty($3, CPPMakeProperty::T_normal,
current_scope, @1.file);
make_property->_get_function = getter->as_function_group();
CPPDeclaration *hasser = $5->find_symbol(current_scope, global_scope, current_lexer);
if (hasser == nullptr || hasser->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("reference to non-existent or invalid has/find method: " + $5->get_fully_scoped_name(), @5);
} else {
make_property->_has_function = hasser->as_function_group();
} }
CPPDeclaration *setter = $9->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *setter = $7->find_symbol(current_scope, global_scope, current_lexer);
if (setter == (CPPDeclaration *)NULL || setter->get_subtype() != CPPDeclaration::ST_function_group) { if (setter == nullptr || setter->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid setter: " + $9->get_fully_scoped_name(), @9); yyerror("reference to non-existent or invalid setter: " + $7->get_fully_scoped_name(), @7);
} else {
make_property->_set_function = setter->as_function_group();
} }
CPPDeclaration *clearer = $11->find_symbol(current_scope, global_scope, current_lexer); CPPDeclaration *clearer = $11->find_symbol(current_scope, global_scope, current_lexer);
if (clearer == (CPPDeclaration *)NULL || clearer->get_subtype() != CPPDeclaration::ST_function_group) { if (clearer == nullptr || clearer->get_subtype() != CPPDeclaration::ST_function_group) {
yyerror("Reference to non-existent or invalid clear-function: " + $11->get_fully_scoped_name(), @11); yyerror("reference to non-existent or invalid clear method: " + $11->get_fully_scoped_name(), @11);
} else {
make_property->_clear_function = clearer->as_function_group();
} }
if (hasser && getter && setter && clearer) {
CPPMakeProperty *make_property;
make_property = new CPPMakeProperty($3,
hasser->as_function_group(),
getter->as_function_group(),
setter->as_function_group(),
clearer->as_function_group(),
current_scope, @1.file);
current_scope->add_declaration(make_property, global_scope, current_lexer, @1); current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
} }
} }
@ -1723,6 +1786,18 @@ maybe_trailing_return_type:
; ;
maybe_comma_identifier:
empty
{
$$ = NULL;
}
| ',' IDENTIFIER
{
$$ = $2;
}
;
function_parameter_list: function_parameter_list:
empty empty
{ {

View File

@ -18,37 +18,17 @@
* *
*/ */
CPPMakeProperty:: CPPMakeProperty::
CPPMakeProperty(CPPIdentifier *ident, CPPMakeProperty(CPPIdentifier *ident, Type type,
CPPFunctionGroup *getter, CPPFunctionGroup *setter,
CPPScope *current_scope, const CPPFile &file) : CPPScope *current_scope, const CPPFile &file) :
CPPDeclaration(file), CPPDeclaration(file),
_ident(ident), _ident(ident),
_length_function(NULL), _type(type),
_has_function(NULL), _length_function(nullptr),
_get_function(getter), _has_function(nullptr),
_set_function(setter), _get_function(nullptr),
_clear_function(NULL), _set_function(nullptr),
_del_function(NULL) _clear_function(nullptr),
{ _del_function(nullptr)
_ident->_native_scope = current_scope;
}
/**
*
*/
CPPMakeProperty::
CPPMakeProperty(CPPIdentifier *ident,
CPPFunctionGroup *hasser, CPPFunctionGroup *getter,
CPPFunctionGroup *setter, CPPFunctionGroup *clearer,
CPPScope *current_scope, const CPPFile &file) :
CPPDeclaration(file),
_ident(ident),
_length_function(NULL),
_has_function(hasser),
_get_function(getter),
_set_function(setter),
_clear_function(clearer),
_del_function(NULL)
{ {
_ident->_native_scope = current_scope; _ident->_native_scope = current_scope;
} }

View File

@ -23,16 +23,72 @@
* This is a MAKE_PROPERTY() declaration appearing within a class body. It * This is a MAKE_PROPERTY() declaration appearing within a class body. It
* means to generate a property within Python, replacing (for instance) * means to generate a property within Python, replacing (for instance)
* get_something()/set_something() with a synthetic 'something' attribute. * get_something()/set_something() with a synthetic 'something' attribute.
*
* This is an example of a simple property (MAKE_PROPERTY is defined as
* the built-in __make_property):
* @@code
* Thing get_thing() const;
* void set_thing(const Thing &);
*
* MAKE_PROPERTY(thing, get_thing, set_thing);
* @@endcode
* The setter may be omitted to make the property read-only.
*
* There is also a secondary macro that allows the property to be set to a
* cleared state using separate clear functions. In the scripting language,
* this would be represented by a "null" value, or an "optional" construct in
* languages that have no notion of a null value.
*
* @@code
* bool has_thing() const;
* Thing get_thing() const;
* void set_thing(const Thing &);
* void clear_thing();
* MAKE_PROPERTY2(thing, has_thing, get_thing, set_thing, clear_thing);
* @@endcode
* As with MAKE_PROPERTY, both the setter and clearer can be omitted to create
* a read-only property.
*
* Thirdly, there is a variant called MAKE_SEQ_PROPERTY. It takes a length
* function as argument and the getter and setter take an index as first
* argument:
* @@code
* size_t get_num_things() const;
* Thing &get_thing(size_t i) const;
* void set_thing(size_t i, Thing value) const;
* void remove_thing(size_t i) const;
*
* MAKE_SEQ_PROPERTY(get_num_things, get_thing, set_thing, remove_thing);
* @@endcode
*
* Lastly, there is the possibility to have properties with key/value
* associations, often called a "map" or "dictionary" in scripting languages:
* @@code
* bool has_thing(string key) const;
* Thing &get_thing(string key) const;
* void set_thing(string key, Thing value) const;
* void clear_thing(string key) const;
*
* MAKE_MAP_PROPERTY(things, has_thing, get_thing, set_thing, clear_thing);
* @@endcode
* You may also replace the "has" function with a "find" function that returns
* an index. If the returned index is negative (or in the case of an unsigned
* integer, the maximum value), the item is assumed not to be present in the
* mapping.
*
* It is also possible to use both MAKE_SEQ_PROPERTY and MAKE_MAP_PROPERTY on
* the same property name. This implies that this property has both a
* sequence and mapping interface.
*/ */
class CPPMakeProperty : public CPPDeclaration { class CPPMakeProperty : public CPPDeclaration {
public: public:
CPPMakeProperty(CPPIdentifier *ident, enum Type {
CPPFunctionGroup *getter, CPPFunctionGroup *setter, T_normal = 0x0,
CPPScope *current_scope, const CPPFile &file); T_sequence = 0x1,
T_mapping = 0x2,
};
CPPMakeProperty(CPPIdentifier *ident, CPPMakeProperty(CPPIdentifier *ident, Type type,
CPPFunctionGroup *hasser, CPPFunctionGroup *getter,
CPPFunctionGroup *setter, CPPFunctionGroup *clearer,
CPPScope *current_scope, const CPPFile &file); CPPScope *current_scope, const CPPFile &file);
virtual string get_simple_name() const; virtual string get_simple_name() const;
@ -46,8 +102,7 @@ public:
virtual CPPMakeProperty *as_make_property(); virtual CPPMakeProperty *as_make_property();
CPPIdentifier *_ident; CPPIdentifier *_ident;
// If length_function is not NULL, this is actually a sequence property, Type _type;
// and the other functions take an additional index argument.
CPPFunctionGroup *_length_function; CPPFunctionGroup *_length_function;
CPPFunctionGroup *_has_function; CPPFunctionGroup *_has_function;
CPPFunctionGroup *_get_function; CPPFunctionGroup *_get_function;

View File

@ -456,6 +456,7 @@ typedef struct _object PyObject;
#define MAKE_PROPERTY2(property_name, ...) __make_property2(property_name, __VA_ARGS__) #define MAKE_PROPERTY2(property_name, ...) __make_property2(property_name, __VA_ARGS__)
#define MAKE_SEQ(seq_name, num_name, element_name) __make_seq(seq_name, num_name, element_name) #define MAKE_SEQ(seq_name, num_name, element_name) __make_seq(seq_name, num_name, element_name)
#define MAKE_SEQ_PROPERTY(property_name, ...) __make_seq_property(property_name, __VA_ARGS__) #define MAKE_SEQ_PROPERTY(property_name, ...) __make_seq_property(property_name, __VA_ARGS__)
#define MAKE_MAP_PROPERTY(property_name, ...) __make_map_property(property_name, __VA_ARGS__)
#define EXTENSION(x) __extension x #define EXTENSION(x) __extension x
#define EXTEND __extension #define EXTEND __extension
#else #else
@ -466,6 +467,7 @@ typedef struct _object PyObject;
#define MAKE_PROPERTY2(property_name, ...) #define MAKE_PROPERTY2(property_name, ...)
#define MAKE_SEQ(seq_name, num_name, element_name) #define MAKE_SEQ(seq_name, num_name, element_name)
#define MAKE_SEQ_PROPERTY(property_name, ...) #define MAKE_SEQ_PROPERTY(property_name, ...)
#define MAKE_MAP_PROPERTY(property_name, ...)
#define EXTENSION(x) #define EXTENSION(x)
#define EXTEND #define EXTEND
#endif #endif

View File

@ -51,6 +51,10 @@ PUBLISHED:
static Filename get_cwd(); static Filename get_cwd();
PUBLISHED:
MAKE_MAP_PROPERTY(environment_variables, has_environment_variable,
get_environment_variable, set_environment_variable);
MAKE_SEQ_PROPERTY(args, get_num_args, get_arg); MAKE_SEQ_PROPERTY(args, get_num_args, get_arg);
MAKE_PROPERTY(binary_name, get_binary_name, set_binary_name); MAKE_PROPERTY(binary_name, get_binary_name, set_binary_name);
MAKE_PROPERTY(dtool_name, get_dtool_name, set_dtool_name); MAKE_PROPERTY(dtool_name, get_dtool_name, set_dtool_name);

View File

@ -75,8 +75,8 @@ InterfaceMaker::MakeSeq::
MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) : MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
_name(name), _name(name),
_imake_seq(imake_seq), _imake_seq(imake_seq),
_length_getter(NULL), _length_getter(nullptr),
_element_getter(NULL) _element_getter(nullptr)
{ {
} }
@ -86,12 +86,10 @@ MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
InterfaceMaker::Property:: InterfaceMaker::Property::
Property(const InterrogateElement &ielement) : Property(const InterrogateElement &ielement) :
_ielement(ielement), _ielement(ielement),
_length_function(NULL), _length_function(nullptr),
_getter(NULL), _has_function(nullptr),
_setter(NULL), _clear_function(nullptr),
_has_function(NULL), _deleter(nullptr),
_clear_function(NULL),
_deleter(NULL),
_has_this(false) _has_this(false)
{ {
} }

View File

@ -126,9 +126,9 @@ public:
Property(const InterrogateElement &ielement); Property(const InterrogateElement &ielement);
const InterrogateElement &_ielement; const InterrogateElement &_ielement;
vector<FunctionRemap *> _getter_remaps;
vector<FunctionRemap *> _setter_remaps;
Function *_length_function; Function *_length_function;
Function *_getter;
Function *_setter;
Function *_has_function; Function *_has_function;
Function *_clear_function; Function *_clear_function;
Function *_deleter; Function *_deleter;

View File

@ -2640,8 +2640,7 @@ write_module_class(ostream &out, Object *obj) {
for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) { for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
Property *property = (*pit); Property *property = (*pit);
const InterrogateElement &ielem = property->_ielement; const InterrogateElement &ielem = property->_ielement;
if (!property->_has_this || if (!property->_has_this || property->_getter_remaps.empty()) {
property->_getter == NULL || !is_function_legal(property->_getter)) {
continue; continue;
} }
@ -2652,8 +2651,7 @@ write_module_class(ostream &out, Object *obj) {
string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter"; string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
string setter = "NULL"; string setter = "NULL";
if (property->_length_function == NULL && if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) {
property->_setter != NULL && is_function_legal(property->_setter)) {
setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter"; setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
} }
@ -3190,8 +3188,7 @@ write_module_class(ostream &out, Object *obj) {
for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) { for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
Property *property = (*pit); Property *property = (*pit);
const InterrogateElement &ielem = property->_ielement; const InterrogateElement &ielem = property->_ielement;
if (property->_has_this || if (property->_has_this || property->_getter_remaps.empty()) {
property->_getter == NULL || !is_function_legal(property->_getter)) {
continue; continue;
} }
@ -3200,8 +3197,7 @@ write_module_class(ostream &out, Object *obj) {
string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter"; string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
string setter = "NULL"; string setter = "NULL";
if (property->_length_function == NULL && if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) {
property->_setter != NULL && is_function_legal(property->_setter)) {
setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter"; setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
} }
@ -5997,7 +5993,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
// this for coercion constructors since they are called by other wrapper // this for coercion constructors since they are called by other wrapper
// functions which already check this on their own. Generated getters // functions which already check this on their own. Generated getters
// obviously can't raise asserts. // obviously can't raise asserts.
if (watch_asserts && (return_flags & RF_coerced) == 0 && if (watch_asserts && (return_flags & (RF_coerced | RF_raise_keyerror)) == 0 &&
remap->_type != FunctionRemap::T_getter && remap->_type != FunctionRemap::T_getter &&
remap->_type != FunctionRemap::T_setter) { remap->_type != FunctionRemap::T_setter) {
out << "#ifndef NDEBUG\n"; out << "#ifndef NDEBUG\n";
@ -6126,6 +6122,27 @@ write_function_instance(ostream &out, FunctionRemap *remap,
indent(out, indent_level) << "manage = true;\n"; indent(out, indent_level) << "manage = true;\n";
indent(out, indent_level) << "return true;\n"; indent(out, indent_level) << "return true;\n";
} }
} else if (return_flags & RF_raise_keyerror) {
CPPType *orig_type = remap->_return_type->get_orig_type();
if (TypeManager::is_bool(orig_type) || TypeManager::is_pointer(orig_type)) {
indent(out, indent_level) << "if (!" << return_expr << ") {\n";
} else if (TypeManager::is_unsigned_integer(orig_type)) {
indent(out, indent_level) << "if ((int)" << return_expr << " == -1) {\n";
} else if (TypeManager::is_integer(orig_type)) {
indent(out, indent_level) << "if (" << return_expr << " < 0) {\n";
} else {
indent(out, indent_level) << "if (false) {\n";
}
if (args_type == AT_single_arg) {
indent(out, indent_level) << " PyErr_SetObject(PyExc_KeyError, arg);\n";
} else {
indent(out, indent_level) << " PyErr_SetObject(PyExc_KeyError, key);\n";
}
error_return(out, indent_level + 2, return_flags);
indent(out, indent_level) << "}\n";
} }
// Close the extra braces opened earlier. // Close the extra braces opened earlier.
@ -6398,19 +6415,21 @@ write_make_seq(ostream &out, Object *obj, const std::string &ClassName,
*/ */
void InterfaceMakerPythonNative:: void InterfaceMakerPythonNative::
write_getset(ostream &out, Object *obj, Property *property) { write_getset(ostream &out, Object *obj, Property *property) {
// We keep around this empty vector for passing to get_call_str.
const vector_string pexprs;
string ClassName = make_safe_name(obj->_itype.get_scoped_name()); string ClassName = make_safe_name(obj->_itype.get_scoped_name());
std::string cClassName = obj->_itype.get_true_name(); std::string cClassName = obj->_itype.get_true_name();
const InterrogateElement &ielem = property->_ielement; const InterrogateElement &ielem = property->_ielement;
if (property->_length_function != NULL) { FunctionRemap *len_remap = nullptr;
if (property->_length_function != nullptr) {
// This is actually a sequence. Wrap this with a special class. // This is actually a sequence. Wrap this with a special class.
FunctionRemap *len_remap = property->_length_function->_remaps.front(); len_remap = property->_length_function->_remaps.front();
vector_string pexprs;
out << "/**\n" out << "/**\n"
" * sequence length function for property " << cClassName << "::" << ielem.get_name() << "\n" " * sequence length function for property " << ielem.get_scoped_name() << "\n"
" */\n" " */\n"
"static Py_ssize_t Dtool_" + ClassName + "_" + ielem.get_name() + "_Len(PyObject *self) {\n"; "static Py_ssize_t Dtool_" + ClassName + "_" + ielem.get_name() + "_Len(PyObject *self) {\n";
if (property->_length_function->_has_this) { if (property->_length_function->_has_this) {
@ -6424,16 +6443,20 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << " return (Py_ssize_t)" << len_remap->get_call_str("", pexprs) << ";\n"; out << " return (Py_ssize_t)" << len_remap->get_call_str("", pexprs) << ";\n";
} }
out << "}\n\n"; out << "}\n\n";
}
// Now write out the getitem helper function. if (property->_getter_remaps.empty()) {
if (property->_getter != NULL) { return;
}
if (ielem.is_sequence()) {
out << out <<
"/**\n" "/**\n"
" * sequence getter for property " << cClassName << "::" << ielem.get_name() << "\n" " * sequence getter for property " << ielem.get_scoped_name() << "\n"
" */\n" " */\n"
"static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getitem(PyObject *self, Py_ssize_t index) {\n"; "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Getitem(PyObject *self, Py_ssize_t index) {\n";
if (property->_getter->_has_this ||
(property->_has_function && property->_has_function->_has_this)) { if (property->_has_this) {
out << out <<
" " << cClassName << " *local_this = NULL;\n" " " << cClassName << " *local_this = NULL;\n"
" if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n" " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
@ -6449,28 +6472,25 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << " return NULL;\n"; out << " return NULL;\n";
out << " }\n"; out << " }\n";
if (property->_has_function != NULL) { /*if (property->_has_function != NULL) {
if (property->_has_function->_has_this) { out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n"
out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n"; << " Py_INCREF(Py_None);\n"
} else {
out << " if (!" << cClassName << "::" << property->_has_function->_ifunc.get_name() << "(index)) {\n";
}
out << " Py_INCREF(Py_None);\n"
<< " return Py_None;\n" << " return Py_None;\n"
<< " }\n"; << " }\n";
} }*/
std::set<FunctionRemap*> remaps; std::set<FunctionRemap*> remaps;
// Extract only the getters that take one argument. // Extract only the getters that take one integral argument.
Function::Remaps::iterator it; Function::Remaps::iterator it;
for (it = property->_getter->_remaps.begin(); for (it = property->_getter_remaps.begin();
it != property->_getter->_remaps.end(); it != property->_getter_remaps.end();
++it) { ++it) {
FunctionRemap *remap = *it; FunctionRemap *remap = *it;
int min_num_args = remap->get_min_num_args(); int min_num_args = remap->get_min_num_args();
int max_num_args = remap->get_max_num_args(); int max_num_args = remap->get_max_num_args();
if (min_num_args <= 1 && max_num_args >= 1) { if (min_num_args <= 1 && max_num_args >= 1 &&
TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type())) {
remaps.insert(remap); remaps.insert(remap);
} }
} }
@ -6485,11 +6505,10 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << ");\n" out << ");\n"
" }\n" " }\n"
"}\n\n"; "}\n\n";
}
// Write out a setitem if this is not a read-only property. // Write out a setitem if this is not a read-only property.
if (property->_setter != NULL) { if (!property->_setter_remaps.empty()) {
out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n"; out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n";
if (property->_has_this) { if (property->_has_this) {
out << " " << cClassName << " *local_this = NULL;\n"; out << " " << cClassName << " *local_this = NULL;\n";
out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \"" out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
@ -6527,13 +6546,14 @@ write_getset(ostream &out, Object *obj, Property *property) {
// Extract only the setters that take two arguments. // Extract only the setters that take two arguments.
Function::Remaps::iterator it; Function::Remaps::iterator it;
for (it = property->_setter->_remaps.begin(); for (it = property->_setter_remaps.begin();
it != property->_setter->_remaps.end(); it != property->_setter_remaps.end();
++it) { ++it) {
FunctionRemap *remap = *it; FunctionRemap *remap = *it;
int min_num_args = remap->get_min_num_args(); int min_num_args = remap->get_min_num_args();
int max_num_args = remap->get_max_num_args(); int max_num_args = remap->get_max_num_args();
if (min_num_args <= 2 && max_num_args >= 2) { if (min_num_args <= 2 && max_num_args >= 2 &&
TypeManager::is_integer(remap->_parameters[1]._remap->get_new_type())) {
remaps.insert(remap); remaps.insert(remap);
} }
} }
@ -6551,8 +6571,192 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << " return -1;\n"; out << " return -1;\n";
out << "}\n\n"; out << "}\n\n";
} }
}
// Now write the getter, which returns a special wrapper object.
// Write the getitem functions.
if (ielem.is_mapping()) {
out <<
"/**\n"
" * mapping getitem for property " << ielem.get_scoped_name() << "\n"
" */\n"
"static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Getitem(PyObject *self, PyObject *arg) {\n";
// Before we do the has_function: if this is also a sequence, then we have
// to also handle the case here that we were passed an index.
if (ielem.is_sequence()) {
out <<
"#if PY_MAJOR_VERSION >= 3\n"
" if (PyLong_CheckExact(arg)) {\n"
"#else\n"
" if (PyLong_CheckExact(arg) || PyInt_CheckExact(arg)) {\n"
"#endif\n"
" return Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem(self, PyLongOrInt_AsSize_t(arg));\n"
" }\n\n";
}
if (property->_has_this) {
out <<
" " << cClassName << " *local_this = NULL;\n"
" if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
" return NULL;\n"
" }\n";
}
if (property->_has_function != NULL) {
std::set<FunctionRemap*> remaps;
remaps.insert(property->_has_function->_remaps.begin(),
property->_has_function->_remaps.end());
out << " {\n";
string expected_params;
write_function_forset(out, remaps, 1, 1, expected_params, 4, true, true,
AT_single_arg, RF_raise_keyerror | RF_err_null, false, true);
out << " }\n";
}
std::set<FunctionRemap*> remaps;
// Extract only the getters that take one argument. Fish out the ones
// already taken by the sequence getter.
Function::Remaps::iterator it;
for (it = property->_getter_remaps.begin();
it != property->_getter_remaps.end();
++it) {
FunctionRemap *remap = *it;
int min_num_args = remap->get_min_num_args();
int max_num_args = remap->get_max_num_args();
if (min_num_args <= 1 && max_num_args >= 1 &&
(!ielem.is_sequence() || !TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type()))) {
remaps.insert(remap);
}
}
string expected_params;
write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true,
AT_single_arg, RF_pyobject | RF_err_null, false, true);
out << " if (!_PyErr_OCCURRED()) {\n";
out << " return Dtool_Raise_BadArgumentsError(\n";
output_quoted(out, 6, expected_params);
out << ");\n"
" }\n"
" return NULL;\n"
"}\n\n";
// Write out a setitem if this is not a read-only property.
if (!property->_setter_remaps.empty()) {
out <<
"/**\n"
" * mapping setitem for property " << ielem.get_scoped_name() << "\n"
" */\n"
"static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Setitem(PyObject *self, PyObject *key, PyObject *value) {\n";
if (property->_has_this) {
out <<
" " << cClassName << " *local_this = NULL;\n"
" if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
<< classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"
" return -1;\n"
" }\n\n";
}
out << " if (value == (PyObject *)NULL) {\n";
if (property->_deleter != NULL) {
out << " PyObject *arg = key;\n";
std::set<FunctionRemap*> remaps;
remaps.insert(property->_deleter->_remaps.begin(),
property->_deleter->_remaps.end());
string expected_params;
write_function_forset(out, remaps, 1, 1,
expected_params, 4, true, true, AT_single_arg,
RF_int, false, false);
out << " return -1;\n";
} else {
out << " Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << "[] attribute\");\n"
" return -1;\n";
}
out << " }\n";
if (property->_clear_function != NULL) {
out << " if (value == Py_None) {\n"
<< " local_this->" << property->_clear_function->_ifunc.get_name() << "(key);\n"
<< " return 0;\n"
<< " }\n";
}
std::set<FunctionRemap*> remaps;
remaps.insert(property->_setter_remaps.begin(),
property->_setter_remaps.end());
// We have to create an args tuple only to unpack it alter, ugh.
out << " PyObject *args = PyTuple_New(2);\n"
<< " PyTuple_SET_ITEM(args, 0, key);\n"
<< " PyTuple_SET_ITEM(args, 1, value);\n"
<< " Py_INCREF(key);\n"
<< " Py_INCREF(value);\n";
string expected_params;
write_function_forset(out, remaps, 2, 2,
expected_params, 2, true, true, AT_varargs,
RF_int | RF_decref_args, false, false);
out << " if (!_PyErr_OCCURRED()) {\n";
out << " Dtool_Raise_BadArgumentsError(\n";
output_quoted(out, 6, expected_params);
out << ");\n";
out << " }\n";
out << " Py_DECREF(args);\n";
out << " return -1;\n";
out << "}\n\n";
}
}
// Now write the actual getter wrapper. It will be a different wrapper
// depending on whether it's a mapping, sequence, or both.
if (ielem.is_mapping() && ielem.is_sequence()) {
out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
if (property->_has_this) {
out << " nassertr(self != NULL, NULL);\n"
" Py_INCREF(self);\n";
} else {
out << " Py_XINCREF(self);\n";
}
out << " Dtool_SeqMapWrapper *wrap = PyObject_New(Dtool_SeqMapWrapper, &Dtool_SeqMapWrapper_Type);\n"
" wrap->_seq._base._self = self;\n"
" wrap->_seq._len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
" wrap->_seq._getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n"
" wrap->_map_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
if (!property->_setter_remaps.empty()) {
out << " wrap->_seq._setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
out << " wrap->_map_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
} else {
out << " wrap->_seq._setitem_func = NULL;\n";
out << " wrap->_map_setitem_func = NULL;\n";
}
out << " return (PyObject *)wrap;\n"
"}\n\n";
} else if (ielem.is_mapping()) {
out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
if (property->_has_this) {
out << " nassertr(self != NULL, NULL);\n"
" Py_INCREF(self);\n";
} else {
out << " Py_XINCREF(self);\n";
}
out << " Dtool_MappingWrapper *wrap = PyObject_New(Dtool_MappingWrapper, &Dtool_MappingWrapper_Type);\n"
" wrap->_base._self = self;\n"
" wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
if (!property->_setter_remaps.empty()) {
out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
} else {
out << " wrap->_setitem_func = NULL;\n";
}
out << " return (PyObject *)wrap;\n"
"}\n\n";
} else if (ielem.is_sequence()) {
out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n"; out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
if (property->_has_this) { if (property->_has_this) {
out << " nassertr(self != NULL, NULL);\n" out << " nassertr(self != NULL, NULL);\n"
@ -6561,21 +6765,21 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << " Py_XINCREF(self);\n"; out << " Py_XINCREF(self);\n";
} }
out << " Dtool_SequenceWrapper *wrap = PyObject_New(Dtool_SequenceWrapper, &Dtool_SequenceWrapper_Type);\n" out << " Dtool_SequenceWrapper *wrap = PyObject_New(Dtool_SequenceWrapper, &Dtool_SequenceWrapper_Type);\n"
" wrap->_base = self;\n" " wrap->_base._self = self;\n"
" wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n" " wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
" wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Getitem;\n"; " wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n";
if (property->_setter != NULL) { if (!property->_setter_remaps.empty()) {
out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Setitem;\n"; out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
} else { } else {
out << " wrap->_setitem_func = NULL;\n"; out << " wrap->_setitem_func = NULL;\n";
} }
out << " return (PyObject *)wrap;\n" out << " return (PyObject *)wrap;\n"
"}\n\n"; "}\n\n";
} else if (property->_getter != NULL) { } else {
// Write out a regular, unwrapped getter. // Write out a regular, unwrapped getter.
out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n"; out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
FunctionRemap *remap = property->_getter->_remaps.front(); FunctionRemap *remap = property->_getter_remaps.front();
if (remap->_has_this) { if (remap->_has_this) {
if (remap->_const_method) { if (remap->_const_method) {
@ -6611,7 +6815,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
out << "}\n\n"; out << "}\n\n";
// Write out a setter if this is not a read-only property. // Write out a setter if this is not a read-only property.
if (property->_setter != NULL) { if (!property->_setter_remaps.empty()) {
out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter(PyObject *self, PyObject *arg, void *) {\n"; out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter(PyObject *self, PyObject *arg, void *) {\n";
if (remap->_has_this) { if (remap->_has_this) {
out << " " << cClassName << " *local_this = NULL;\n"; out << " " << cClassName << " *local_this = NULL;\n";
@ -6649,8 +6853,8 @@ write_getset(ostream &out, Object *obj, Property *property) {
// Extract only the setters that take one argument. // Extract only the setters that take one argument.
Function::Remaps::iterator it; Function::Remaps::iterator it;
for (it = property->_setter->_remaps.begin(); for (it = property->_setter_remaps.begin();
it != property->_setter->_remaps.end(); it != property->_setter_remaps.end();
++it) { ++it) {
FunctionRemap *remap = *it; FunctionRemap *remap = *it;
int min_num_args = remap->get_min_num_args(); int min_num_args = remap->get_min_num_args();
@ -6763,23 +6967,91 @@ record_object(TypeIndex type_index) {
ElementIndex element_index = itype.get_element(ei); ElementIndex element_index = itype.get_element(ei);
const InterrogateElement &ielement = idb->get_element(element_index); const InterrogateElement &ielement = idb->get_element(element_index);
Property *property = new Property(ielement); Property *property = record_property(itype, itype.get_element(ei));
if (property != nullptr) {
object->_properties.push_back(property);
} else {
// No use exporting a property without a getter.
delete property;
}
}
int num_make_seqs = itype.number_of_make_seqs();
for (int msi = 0; msi < num_make_seqs; msi++) {
MakeSeqIndex make_seq_index = itype.get_make_seq(msi);
const InterrogateMakeSeq &imake_seq = idb->get_make_seq(make_seq_index);
string class_name = itype.get_scoped_name();
string clean_name = InterrogateBuilder::clean_identifier(class_name);
string wrapper_name = "MakeSeq_" + clean_name + "_" + imake_seq.get_name();
MakeSeq *make_seq = new MakeSeq(wrapper_name, imake_seq);
make_seq->_length_getter = record_function(itype, imake_seq.get_length_getter());
make_seq->_element_getter = record_function(itype, imake_seq.get_element_getter());
object->_make_seqs.push_back(make_seq);
}
object->check_protocols();
int num_nested = itype.number_of_nested_types();
for (int ni = 0; ni < num_nested; ni++) {
TypeIndex nested_index = itype.get_nested_type(ni);
record_object(nested_index);
}
return object;
}
/**
*
*/
InterfaceMaker::Property *InterfaceMakerPythonNative::
record_property(const InterrogateType &itype, ElementIndex element_index) {
InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
const InterrogateElement &ielement = idb->get_element(element_index);
if (!ielement.has_getter()) {
// A property needs at the very least a getter.
return nullptr;
}
Property *property;
{
FunctionIndex func_index = ielement.get_getter();
if (func_index != 0) {
const InterrogateFunction &ifunc = idb->get_function(func_index);
property = new Property(ielement);
InterrogateFunction::Instances::const_iterator ii;
for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) {
CPPInstance *cppfunc = (*ii).second;
FunctionRemap *remap =
make_function_remap(itype, ifunc, cppfunc, 0);
if (remap != nullptr && is_remap_legal(remap)) {
property->_getter_remaps.push_back(remap);
property->_has_this |= remap->_has_this;
}
}
} else {
return nullptr;
}
}
if (ielement.has_setter()) { if (ielement.has_setter()) {
FunctionIndex func_index = ielement.get_setter(); FunctionIndex func_index = ielement.get_setter();
Function *setter = record_function(itype, func_index); if (func_index != 0) {
if (is_function_legal(setter)) { const InterrogateFunction &ifunc = idb->get_function(func_index);
property->_setter = setter;
property->_has_this |= setter->_has_this;
}
}
if (ielement.has_getter()) { InterrogateFunction::Instances::const_iterator ii;
FunctionIndex func_index = ielement.get_getter(); for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) {
Function *getter = record_function(itype, func_index); CPPInstance *cppfunc = (*ii).second;
if (is_function_legal(getter)) { FunctionRemap *remap =
property->_getter = getter; make_function_remap(itype, ifunc, cppfunc, 0);
property->_has_this |= getter->_has_this;
if (remap != nullptr && is_remap_legal(remap)) {
property->_setter_remaps.push_back(remap);
property->_has_this |= remap->_has_this;
}
}
} }
} }
@ -6813,43 +7085,11 @@ record_object(TypeIndex type_index) {
if (ielement.is_sequence()) { if (ielement.is_sequence()) {
FunctionIndex func_index = ielement.get_length_function(); FunctionIndex func_index = ielement.get_length_function();
property->_length_function = record_function(itype, func_index); property->_length_function = record_function(itype, func_index);
if (property->_length_function != nullptr) {
property->_has_this |= property->_length_function->_has_this;
}
} }
if (property->_getter != NULL) { return property;
object->_properties.push_back(property);
} else {
// No use exporting a property without a getter.
delete property;
}
}
int num_make_seqs = itype.number_of_make_seqs();
for (int msi = 0; msi < num_make_seqs; msi++) {
MakeSeqIndex make_seq_index = itype.get_make_seq(msi);
const InterrogateMakeSeq &imake_seq = idb->get_make_seq(make_seq_index);
string class_name = itype.get_scoped_name();
string clean_name = InterrogateBuilder::clean_identifier(class_name);
string wrapper_name = "MakeSeq_" + clean_name + "_" + imake_seq.get_name();
MakeSeq *make_seq = new MakeSeq(wrapper_name, imake_seq);
make_seq->_length_getter = record_function(itype, imake_seq.get_length_getter());
make_seq->_element_getter = record_function(itype, imake_seq.get_element_getter());
object->_make_seqs.push_back(make_seq);
}
object->check_protocols();
int num_nested = itype.number_of_nested_types();
for (int ni = 0; ni < num_nested; ni++) {
TypeIndex nested_index = itype.get_nested_type(ni);
record_object(nested_index);
}
return object;
} }
/** /**
* Walks through the set of functions in the database and generates wrappers * Walks through the set of functions in the database and generates wrappers
* for each function, storing these in the database. No actual code should be * for each function, storing these in the database. No actual code should be

View File

@ -47,6 +47,7 @@ public:
virtual bool separate_overloading(); virtual bool separate_overloading();
virtual Object *record_object(TypeIndex type_index); virtual Object *record_object(TypeIndex type_index);
Property *record_property(const InterrogateType &itype, ElementIndex element_index);
protected: protected:
virtual string get_wrapper_prefix(); virtual string get_wrapper_prefix();
@ -111,6 +112,9 @@ private:
// Decref temporary args object before returning. // Decref temporary args object before returning.
RF_decref_args = 0x1000, RF_decref_args = 0x1000,
// This raises a KeyError on falsey (or -1) return value.
RF_raise_keyerror = 0x4000,
}; };
class SlottedFunctionDef { class SlottedFunctionDef {

View File

@ -1798,25 +1798,36 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
} }
string property_name = make_property->get_local_name(&parser); string property_name = make_property->get_local_name(&parser);
InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
// First, check to see if it's already there. // First, check to see if it's already there.
ElementIndex index = 0;
PropertiesByName::const_iterator tni = PropertiesByName::const_iterator tni =
_properties_by_name.find(property_name); _properties_by_name.find(property_name);
if (tni != _properties_by_name.end()) { if (tni != _properties_by_name.end()) {
ElementIndex index = (*tni).second; index = (*tni).second;
const InterrogateElement &ielem = idb->get_element(index);
if (ielem._make_property == make_property) {
// This is the same property.
return index; return index;
} }
// It is possible to have property definitions with the same name, but
// they cannot define conflicting interfaces.
if ((ielem.is_sequence() || ielem.is_mapping()) !=
(make_property->_type != CPPMakeProperty::T_normal)) {
cerr << "Conflicting property definitions for " << property_name << "!\n";
return index;
}
}
// If we have a length function (ie. this is a sequence property), we should // If we have a length function (ie. this is a sequence property), we should
// find the function that will give us the length. // find the function that will give us the length.
FunctionIndex length_function = 0; FunctionIndex length_function = 0;
bool is_seq = false; if (make_property->_type & CPPMakeProperty::T_sequence) {
CPPFunctionGroup::Instances::const_iterator fi; CPPFunctionGroup::Instances::const_iterator fi;
CPPFunctionGroup *fgroup = make_property->_length_function; CPPFunctionGroup *fgroup = make_property->_length_function;
if (fgroup != NULL) { if (fgroup != NULL) {
is_seq = true;
for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
CPPInstance *function = (*fi); CPPInstance *function = (*fi);
CPPFunctionType *ftype = CPPFunctionType *ftype =
@ -1829,6 +1840,7 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
} }
} }
} }
}
if (length_function == 0) { if (length_function == 0) {
cerr << "No instance of length method '" cerr << "No instance of length method '"
<< fgroup->_name << "' is suitable!\n"; << fgroup->_name << "' is suitable!\n";
@ -1840,7 +1852,10 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
CPPInstance *getter = NULL; CPPInstance *getter = NULL;
CPPType *return_type = NULL; CPPType *return_type = NULL;
fgroup = make_property->_get_function; // How many arguments we expect the getter to have.
size_t num_args = (size_t)(make_property->_type != CPPMakeProperty::T_normal);
CPPFunctionGroup *fgroup = make_property->_get_function;
if (fgroup != NULL) { if (fgroup != NULL) {
CPPFunctionGroup::Instances::const_iterator fi; CPPFunctionGroup::Instances::const_iterator fi;
for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
@ -1852,9 +1867,13 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
const CPPParameterList::Parameters &params = ftype->_parameters->_parameters; const CPPParameterList::Parameters &params = ftype->_parameters->_parameters;
size_t expected_num_args = (size_t)is_seq; size_t expected_num_args = 0;
size_t index_arg = 0; size_t index_arg = 0;
if (make_property->_type != CPPMakeProperty::T_normal) {
++expected_num_args;
}
if (!params.empty() && params[0]->get_simple_name() == "self" && if (!params.empty() && params[0]->get_simple_name() == "self" &&
TypeManager::is_pointer_to_PyObject(params[0]->_type)) { TypeManager::is_pointer_to_PyObject(params[0]->_type)) {
// Taking a PyObject *self argument. // Taking a PyObject *self argument.
@ -1867,7 +1886,8 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
(params.size() > expected_num_args && (params.size() > expected_num_args &&
params[expected_num_args]->_initializer != NULL)) { params[expected_num_args]->_initializer != NULL)) {
// If this is a sequence getter, it must take an index argument. // If this is a sequence getter, it must take an index argument.
if (is_seq && !TypeManager::is_integer(params[index_arg]->_type)) { if (make_property->_type == CPPMakeProperty::T_sequence &&
!TypeManager::is_integer(params[index_arg]->_type)) {
continue; continue;
} }
@ -1899,13 +1919,14 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
CPPInstance *function = (*fi); CPPInstance *function = (*fi);
CPPFunctionType *ftype = CPPFunctionType *ftype =
function->_type->as_function_type(); function->_type->as_function_type();
if (ftype != NULL && TypeManager::is_bool(ftype->_return_type)) { if (ftype != nullptr && (TypeManager::is_integer(ftype->_return_type) ||
TypeManager::is_pointer(ftype->_return_type))) {
hasser = function; hasser = function;
break; break;
} }
} }
if (hasser == NULL || return_type == NULL) { if (hasser == nullptr) {
cerr << "No instance of has-function '" cerr << "No instance of has-function '"
<< fgroup->_name << "' is suitable!\n"; << fgroup->_name << "' is suitable!\n";
return 0; return 0;
@ -1921,45 +1942,78 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) {
CPPInstance *function = (*fi); CPPInstance *function = (*fi);
CPPFunctionType *ftype = function->_type->as_function_type(); CPPFunctionType *ftype = function->_type->as_function_type();
if (ftype != NULL && ftype->_parameters->_parameters.size() == (size_t)is_seq) { if (ftype != NULL && ftype->_parameters->_parameters.size() == num_args) {
deleter = function; deleter = function;
break; break;
} }
} }
if (deleter == NULL || return_type == NULL) { if (deleter == nullptr) {
cerr << "No instance of delete-function '" cerr << "No instance of delete-function '"
<< fgroup->_name << "' is suitable!\n"; << fgroup->_name << "' is suitable!\n";
return 0; return 0;
} }
} }
InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); if (index == 0) {
// It isn't here, so we'll have to define it. // It isn't here, so we'll have to define it.
ElementIndex index = idb->get_next_index(); index = idb->get_next_index();
_properties_by_name[property_name] = index; _properties_by_name[property_name] = index;
InterrogateElement iproperty; InterrogateElement iproperty;
iproperty._name = make_property->get_simple_name(); iproperty._name = make_property->get_simple_name();
iproperty._scoped_name = descope(make_property->get_local_name(&parser)); iproperty._scoped_name = descope(make_property->get_local_name(&parser));
idb->add_element(index, iproperty);
}
InterrogateElement &iproperty = idb->update_element(index);
if (return_type != NULL) { if (return_type != NULL) {
iproperty._type = get_type(TypeManager::unwrap_reference(return_type), false); TypeIndex return_index = get_type(TypeManager::unwrap_reference(return_type), false);
if (iproperty._type != 0 && iproperty._type != return_index) {
cerr << "Property " << property_name << " has inconsistent element type!\n";
}
} else { } else {
iproperty._type = 0; iproperty._type = 0;
} }
if (length_function != 0) { if (make_property->_type & CPPMakeProperty::T_sequence) {
iproperty._flags |= InterrogateElement::F_sequence; iproperty._flags |= InterrogateElement::F_sequence;
iproperty._length_function = length_function; iproperty._length_function = length_function;
} }
if (make_property->_type & CPPMakeProperty::T_mapping) {
iproperty._flags |= InterrogateElement::F_mapping;
}
if (make_property->_type == CPPMakeProperty::T_normal) {
if (getter != NULL) { if (getter != NULL) {
iproperty._flags |= InterrogateElement::F_has_getter; iproperty._flags |= InterrogateElement::F_has_getter;
iproperty._getter = get_function(getter, "", struct_type, iproperty._getter = get_function(getter, "", struct_type,
struct_type->get_scope(), 0); struct_type->get_scope(), 0);
nassertr(iproperty._getter, 0); nassertr(iproperty._getter, 0);
} }
} else {
// We could have a mixed sequence/mapping property, so synthesize a
// getitem function. We don't really care what's in here; we just use
// this to store the remaps.
if (!iproperty.has_getter()) {
iproperty._flags |= InterrogateElement::F_has_getter;
iproperty._getter = InterrogateDatabase::get_ptr()->get_next_index();
InterrogateFunction *ifunction = new InterrogateFunction;
ifunction->_instances = new InterrogateFunction::Instances;
InterrogateDatabase::get_ptr()->add_function(iproperty._getter, ifunction);
}
// Add our getter to the generated getitem function.
string signature = TypeManager::get_function_signature(getter);
InterrogateFunction &ifunction =
InterrogateDatabase::get_ptr()->update_function(iproperty._getter);
if (ifunction._instances == nullptr) {
ifunction._instances = new InterrogateFunction::Instances;
}
ifunction._instances->insert(InterrogateFunction::Instances::value_type(signature, getter));
}
if (hasser != NULL) { if (hasser != NULL) {
iproperty._flags |= InterrogateElement::F_has_has_function; iproperty._flags |= InterrogateElement::F_has_has_function;
@ -2011,7 +2065,6 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
} }
} }
idb->add_element(index, iproperty);
return index; return index;
} }
@ -2621,7 +2674,9 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
} else if ((*di)->get_subtype() == CPPDeclaration::ST_make_property) { } else if ((*di)->get_subtype() == CPPDeclaration::ST_make_property) {
ElementIndex element_index = get_make_property((*di)->as_make_property(), cpptype, scope); ElementIndex element_index = get_make_property((*di)->as_make_property(), cpptype, scope);
if (find(itype._elements.begin(), itype._elements.end(), element_index) == itype._elements.end()) {
itype._elements.push_back(element_index); itype._elements.push_back(element_index);
}
} else if ((*di)->get_subtype() == CPPDeclaration::ST_make_seq) { } else if ((*di)->get_subtype() == CPPDeclaration::ST_make_seq) {
MakeSeqIndex make_seq_index = get_make_seq((*di)->as_make_seq(), cpptype); MakeSeqIndex make_seq_index = get_make_seq((*di)->as_make_seq(), cpptype);

View File

@ -26,6 +26,7 @@ InterrogateElement(InterrogateModuleDef *def) :
_clear_function = 0; _clear_function = 0;
_del_function = 0; _del_function = 0;
_length_function = 0; _length_function = 0;
_make_property = nullptr;
} }
/** /**
@ -52,6 +53,7 @@ operator = (const InterrogateElement &copy) {
_clear_function = copy._clear_function; _clear_function = copy._clear_function;
_del_function = copy._del_function; _del_function = copy._del_function;
_length_function = copy._length_function; _length_function = copy._length_function;
_make_property = copy._make_property;
} }
/** /**
@ -199,6 +201,14 @@ get_length_function() const {
return _length_function; return _length_function;
} }
/**
*
*/
INLINE bool InterrogateElement::
is_mapping() const {
return (_flags & F_mapping) != 0;
}
INLINE ostream & INLINE ostream &
operator << (ostream &out, const InterrogateElement &element) { operator << (ostream &out, const InterrogateElement &element) {

View File

@ -19,6 +19,7 @@
#include "interrogateComponent.h" #include "interrogateComponent.h"
class IndexRemapper; class IndexRemapper;
class CPPMakeProperty;
/** /**
* An internal representation of a data element, like a data member or a * An internal representation of a data element, like a data member or a
@ -51,6 +52,7 @@ public:
INLINE FunctionIndex get_del_function() const; INLINE FunctionIndex get_del_function() const;
INLINE bool is_sequence() const; INLINE bool is_sequence() const;
INLINE FunctionIndex get_length_function() const; INLINE FunctionIndex get_length_function() const;
INLINE bool is_mapping() const;
void output(ostream &out) const; void output(ostream &out) const;
void input(istream &in); void input(istream &in);
@ -80,6 +82,8 @@ private:
FunctionIndex _clear_function; FunctionIndex _clear_function;
FunctionIndex _del_function; FunctionIndex _del_function;
CPPMakeProperty *_make_property;
friend class InterrogateBuilder; friend class InterrogateBuilder;
}; };

View File

@ -614,6 +614,14 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_SequenceWrapper)"); return Dtool_Raise_TypeError("PyType_Ready(Dtool_SequenceWrapper)");
} }
if (PyType_Ready(&Dtool_MappingWrapper_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper)");
}
if (PyType_Ready(&Dtool_SeqMapWrapper_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_SeqMapWrapper)");
}
if (PyType_Ready(&Dtool_StaticProperty_Type) < 0) { if (PyType_Ready(&Dtool_StaticProperty_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)"); return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)");
} }
@ -1028,34 +1036,79 @@ bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds)
} }
/** /**
* This class is returned from properties that require a settable interface, * These classes are returned from properties that require a subscript
* ie. something.children[i] = 3. * interface, ie. something.children[i] = 3.
*/ */
static void Dtool_SequenceWrapper_dealloc(PyObject *self) { static void Dtool_WrapperBase_dealloc(PyObject *self) {
Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
nassertv(wrap); nassertv(wrap);
Py_XDECREF(wrap->_base); Py_XDECREF(wrap->_self);
} }
static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) { static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) {
Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
nassertr(wrap, -1); nassertr(wrap, -1);
if (wrap->_len_func != nullptr) {
nassertr(wrap->_len_func, -1); nassertr(wrap->_len_func, -1);
return wrap->_len_func(wrap->_base); return wrap->_len_func(wrap->_base._self);
} else {
Dtool_Raise_TypeError("property does not support len()");
return -1;
}
} }
static PyObject *Dtool_SequenceWrapper_getitem(PyObject *self, Py_ssize_t index) { static PyObject *Dtool_SequenceWrapper_getitem(PyObject *self, Py_ssize_t index) {
Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
nassertr(wrap, NULL); nassertr(wrap, nullptr);
nassertr(wrap->_getitem_func, NULL); nassertr(wrap->_getitem_func, nullptr);
return wrap->_getitem_func(wrap->_base, index); return wrap->_getitem_func(wrap->_base._self, index);
} }
static int Dtool_SequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) { static int Dtool_SequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) {
Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self; Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
nassertr(wrap, -1); nassertr(wrap, -1);
nassertr(wrap->_setitem_func, -1); if (wrap->_setitem_func != nullptr) {
return wrap->_setitem_func(wrap->_base, index, value); return wrap->_setitem_func(wrap->_base._self, index, value);
} else {
Dtool_Raise_TypeError("property does not support item assignment");
return -1;
}
}
static PyObject *Dtool_MappingWrapper_getitem(PyObject *self, PyObject *key) {
Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
nassertr(wrap, nullptr);
nassertr(wrap->_getitem_func, nullptr);
return wrap->_getitem_func(wrap->_base._self, key);
}
static int Dtool_MappingWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
nassertr(wrap, -1);
if (wrap->_setitem_func != nullptr) {
return wrap->_setitem_func(wrap->_base._self, key, value);
} else {
Dtool_Raise_TypeError("property does not support item assignment");
return -1;
}
}
static PyObject *Dtool_SeqMapWrapper_getitem(PyObject *self, PyObject *key) {
Dtool_SeqMapWrapper *wrap = (Dtool_SeqMapWrapper *)self;
nassertr(wrap, nullptr);
nassertr(wrap->_map_getitem_func, nullptr);
return wrap->_map_getitem_func(wrap->_seq._base._self, key);
}
static int Dtool_SeqMapWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
Dtool_SeqMapWrapper *wrap = (Dtool_SeqMapWrapper *)self;
nassertr(wrap, -1);
if (wrap->_map_setitem_func != nullptr) {
return wrap->_map_setitem_func(wrap->_seq._base._self, key, value);
} else {
Dtool_Raise_TypeError("property does not support item assignment");
return -1;
}
} }
static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = { static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
@ -1071,12 +1124,27 @@ static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
0, // sq_inplace_repeat 0, // sq_inplace_repeat
}; };
static PyMappingMethods Dtool_MappingWrapper_MappingMethods = {
0, // mp_length
Dtool_MappingWrapper_getitem,
Dtool_MappingWrapper_setitem,
};
static PyMappingMethods Dtool_SeqMapWrapper_MappingMethods = {
Dtool_SequenceWrapper_length,
Dtool_SeqMapWrapper_getitem,
Dtool_SeqMapWrapper_setitem,
};
/**
* This variant defines only a sequence interface.
*/
PyTypeObject Dtool_SequenceWrapper_Type = { PyTypeObject Dtool_SequenceWrapper_Type = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"sequence wrapper", "sequence wrapper",
sizeof(Dtool_SequenceWrapper), sizeof(Dtool_SequenceWrapper),
0, // tp_itemsize 0, // tp_itemsize
Dtool_SequenceWrapper_dealloc, Dtool_WrapperBase_dealloc,
0, // tp_print 0, // tp_print
0, // tp_getattr 0, // tp_getattr
0, // tp_setattr 0, // tp_setattr
@ -1262,4 +1330,128 @@ PyTypeObject Dtool_StaticProperty_Type = {
#endif #endif
}; };
/**
* This variant defines only a mapping interface.
*/
PyTypeObject Dtool_MappingWrapper_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"mapping wrapper",
sizeof(Dtool_MappingWrapper),
0, // tp_itemsize
Dtool_WrapperBase_dealloc,
0, // tp_print
0, // tp_getattr
0, // tp_setattr
#if PY_MAJOR_VERSION >= 3
0, // tp_reserved
#else
0, // tp_compare
#endif
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
&Dtool_MappingWrapper_MappingMethods,
0, // tp_hash
0, // tp_call
0, // tp_str
PyObject_GenericGetAttr,
PyObject_GenericSetAttr,
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
0, // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
0, // tp_methods
0, // tp_members
0, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
0, // tp_init
PyType_GenericAlloc,
0, // tp_new
PyObject_Del,
0, // tp_is_gc
0, // tp_bases
0, // tp_mro
0, // tp_cache
0, // tp_subclasses
0, // tp_weaklist
0, // tp_del
#if PY_VERSION_HEX >= 0x02060000
0, // tp_version_tag
#endif
#if PY_VERSION_HEX >= 0x03040000
0, // tp_finalize
#endif
};
/**
* This variant defines both a sequence and mapping interface.
*/
PyTypeObject Dtool_SeqMapWrapper_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"sequence/mapping wrapper",
sizeof(Dtool_SeqMapWrapper),
0, // tp_itemsize
Dtool_WrapperBase_dealloc,
0, // tp_print
0, // tp_getattr
0, // tp_setattr
#if PY_MAJOR_VERSION >= 3
0, // tp_reserved
#else
0, // tp_compare
#endif
0, // tp_repr
0, // tp_as_number
&Dtool_SequenceWrapper_SequenceMethods,
&Dtool_SeqMapWrapper_MappingMethods,
0, // tp_hash
0, // tp_call
0, // tp_str
PyObject_GenericGetAttr,
PyObject_GenericSetAttr,
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
0, // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
0, // tp_methods
0, // tp_members
0, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
0, // tp_init
PyType_GenericAlloc,
0, // tp_new
PyObject_Del,
0, // tp_is_gc
0, // tp_bases
0, // tp_mro
0, // tp_cache
0, // tp_subclasses
0, // tp_weaklist
0, // tp_del
#if PY_VERSION_HEX >= 0x02060000
0, // tp_version_tag
#endif
#if PY_VERSION_HEX >= 0x03040000
0, // tp_finalize
#endif
};
#endif // HAVE_PYTHON #endif // HAVE_PYTHON

View File

@ -339,6 +339,9 @@ EXPCL_INTERROGATEDB PyObject *_Dtool_Raise_BadArgumentsError();
#define Dtool_Raise_BadArgumentsError(x) Dtool_Raise_TypeError("Arguments must match:\n" x) #define Dtool_Raise_BadArgumentsError(x) Dtool_Raise_TypeError("Arguments must match:\n" x)
#endif #endif
// These functions are similar to Dtool_WrapValue, except that they also
// contain code for checking assertions and exceptions when compiling with
// NDEBUG mode on.
EXPCL_INTERROGATEDB PyObject *_Dtool_Return_None(); EXPCL_INTERROGATEDB PyObject *_Dtool_Return_None();
EXPCL_INTERROGATEDB PyObject *Dtool_Return_Bool(bool value); EXPCL_INTERROGATEDB PyObject *Dtool_Return_Bool(bool value);
EXPCL_INTERROGATEDB PyObject *_Dtool_Return(PyObject *value); EXPCL_INTERROGATEDB PyObject *_Dtool_Return(PyObject *value);
@ -465,18 +468,36 @@ EXPCL_INTERROGATEDB PyObject *
map_deepcopy_to_copy(PyObject *self, PyObject *args); map_deepcopy_to_copy(PyObject *self, PyObject *args);
/** /**
* This class is returned from properties that require a settable interface, * These classes are returned from properties that require a subscript
* ie. something.children[i] = 3. * interface, ie. something.children[i] = 3.
*/ */
struct Dtool_WrapperBase {
PyObject_HEAD;
PyObject *_self;
};
struct Dtool_SequenceWrapper { struct Dtool_SequenceWrapper {
PyObject_HEAD Dtool_WrapperBase _base;
PyObject *_base;
lenfunc _len_func; lenfunc _len_func;
ssizeargfunc _getitem_func; ssizeargfunc _getitem_func;
ssizeobjargproc _setitem_func; ssizeobjargproc _setitem_func;
}; };
struct Dtool_MappingWrapper {
Dtool_WrapperBase _base;
binaryfunc _getitem_func;
objobjargproc _setitem_func;
};
struct Dtool_SeqMapWrapper {
Dtool_SequenceWrapper _seq;
binaryfunc _map_getitem_func;
objobjargproc _map_setitem_func;
};
EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type; EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type;
EXPCL_INTERROGATEDB extern PyTypeObject Dtool_MappingWrapper_Type;
EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SeqMapWrapper_Type;
EXPCL_INTERROGATEDB extern PyTypeObject Dtool_StaticProperty_Type; EXPCL_INTERROGATEDB extern PyTypeObject Dtool_StaticProperty_Type;
EXPCL_INTERROGATEDB PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset); EXPCL_INTERROGATEDB PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);

View File

@ -44,12 +44,13 @@ PUBLISHED:
AnimGroup *get_child(int n) const; AnimGroup *get_child(int n) const;
MAKE_SEQ(get_children, get_num_children, get_child); MAKE_SEQ(get_children, get_num_children, get_child);
MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
AnimGroup *get_child_named(const string &name) const; AnimGroup *get_child_named(const string &name) const;
AnimGroup *find_child(const string &name) const; AnimGroup *find_child(const string &name) const;
void sort_descendants(); void sort_descendants();
MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
MAKE_MAP_PROPERTY(children, get_child_named, get_child_named);
public: public:
virtual TypeHandle get_value_type() const; virtual TypeHandle get_value_type() const;

View File

@ -70,12 +70,14 @@ PUBLISHED:
int get_num_children() const; int get_num_children() const;
PartGroup *get_child(int n) const; PartGroup *get_child(int n) const;
MAKE_SEQ(get_children, get_num_children, get_child); MAKE_SEQ(get_children, get_num_children, get_child);
MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
PartGroup *get_child_named(const string &name) const; PartGroup *get_child_named(const string &name) const;
PartGroup *find_child(const string &name) const; PartGroup *find_child(const string &name) const;
void sort_descendants(); void sort_descendants();
MAKE_SEQ_PROPERTY(children, get_num_children, get_child);
MAKE_MAP_PROPERTY(children, get_child_named, get_child_named);
bool apply_freeze(const TransformState *transform); bool apply_freeze(const TransformState *transform);
virtual bool apply_freeze_matrix(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale); virtual bool apply_freeze_matrix(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale);
virtual bool apply_freeze_scalar(PN_stdfloat value); virtual bool apply_freeze_scalar(PN_stdfloat value);

View File

@ -125,6 +125,9 @@ PUBLISHED:
MAKE_SEQ_PROPERTY(points, get_num_points, get_point); MAKE_SEQ_PROPERTY(points, get_num_points, get_point);
MAKE_SEQ_PROPERTY(vectors, get_num_vectors, get_vector); MAKE_SEQ_PROPERTY(vectors, get_num_vectors, get_vector);
// We also define this as a mapping interface, for lookups by name.
MAKE_MAP_PROPERTY(columns, has_column, get_column);
void output(ostream &out) const; void output(ostream &out) const;
void write(ostream &out, int indent_level = 0) const; void write(ostream &out, int indent_level = 0) const;
void write_with_data(ostream &out, int indent_level, void write_with_data(ostream &out, int indent_level,

View File

@ -539,6 +539,8 @@ PUBLISHED:
void set_aux_data(const string &key, TypedReferenceCount *aux_data); void set_aux_data(const string &key, TypedReferenceCount *aux_data);
void clear_aux_data(const string &key); void clear_aux_data(const string &key);
TypedReferenceCount *get_aux_data(const string &key) const; TypedReferenceCount *get_aux_data(const string &key) const;
MAKE_MAP_PROPERTY(aux_data, get_aux_data, get_aux_data,
set_aux_data, clear_aux_data);
INLINE static void set_textures_power_2(AutoTextureScale scale); INLINE static void set_textures_power_2(AutoTextureScale scale);
INLINE static AutoTextureScale get_textures_power_2(); INLINE static AutoTextureScale get_textures_power_2();

View File

@ -142,6 +142,17 @@ get_weight(size_t n) const {
return _entries[n]._weight; return _entries[n]._weight;
} }
/**
* Removes the nth transform stored in the blend object.
*/
INLINE void TransformBlend::
remove_transform(size_t n) {
nassertv(n < _entries.size());
_entries.erase(_entries.begin() + n);
Thread *current_thread = Thread::get_current_thread();
clear_result(current_thread);
}
/** /**
* Replaces the nth transform stored in the blend object. * Replaces the nth transform stored in the blend object.
*/ */

View File

@ -62,9 +62,15 @@ PUBLISHED:
INLINE const VertexTransform *get_transform(size_t n) const; INLINE const VertexTransform *get_transform(size_t n) const;
MAKE_SEQ(get_transforms, get_num_transforms, get_transform); MAKE_SEQ(get_transforms, get_num_transforms, get_transform);
INLINE PN_stdfloat get_weight(size_t n) const; INLINE PN_stdfloat get_weight(size_t n) const;
INLINE void remove_transform(size_t n);
INLINE void set_transform(size_t n, const VertexTransform *transform); INLINE void set_transform(size_t n, const VertexTransform *transform);
INLINE void set_weight(size_t n, PN_stdfloat weight); INLINE void set_weight(size_t n, PN_stdfloat weight);
MAKE_SEQ_PROPERTY(transforms, get_num_transforms, get_transform,
set_transform, remove_transform);
MAKE_SEQ_PROPERTY(weights, get_num_transforms, get_weight, set_weight);
MAKE_MAP_PROPERTY(weights, has_transform, get_weight);
INLINE void update_blend(Thread *current_thread) const; INLINE void update_blend(Thread *current_thread) const;
INLINE void get_blend(LMatrix4 &result, Thread *current_thread) const; INLINE void get_blend(LMatrix4 &result, Thread *current_thread) const;

View File

@ -91,12 +91,16 @@ PUBLISHED:
void clear_tag_states(); void clear_tag_states();
bool has_tag_state(const string &tag_state) const; bool has_tag_state(const string &tag_state) const;
CPT(RenderState) get_tag_state(const string &tag_state) const; CPT(RenderState) get_tag_state(const string &tag_state) const;
MAKE_MAP_PROPERTY(tag_states, has_tag_state, get_tag_state,
set_tag_state, clear_tag_state);
void set_aux_scene_data(const NodePath &node_path, AuxSceneData *data); void set_aux_scene_data(const NodePath &node_path, AuxSceneData *data);
bool clear_aux_scene_data(const NodePath &node_path); bool clear_aux_scene_data(const NodePath &node_path);
AuxSceneData *get_aux_scene_data(const NodePath &node_path) const; AuxSceneData *get_aux_scene_data(const NodePath &node_path) const;
void list_aux_scene_data(ostream &out) const; void list_aux_scene_data(ostream &out) const;
int cleanup_aux_scene_data(Thread *current_thread = Thread::get_current_thread()); int cleanup_aux_scene_data(Thread *current_thread = Thread::get_current_thread());
MAKE_MAP_PROPERTY(aux_scene_data, get_aux_scene_data, get_aux_scene_data,
set_aux_scene_data, clear_aux_scene_data);
private: private:
void add_display_region(DisplayRegion *display_region); void add_display_region(DisplayRegion *display_region);

View File

@ -913,6 +913,9 @@ PUBLISHED:
INLINE bool has_net_tag(const string &key) const; INLINE bool has_net_tag(const string &key) const;
NodePath find_net_tag(const string &key) const; NodePath find_net_tag(const string &key) const;
MAKE_MAP_PROPERTY(tags, has_tag, get_tag, set_tag, clear_tag);
MAKE_MAP_PROPERTY(net_tags, has_net_tag, get_net_tag);
EXTENSION(INLINE PyObject *get_tag_keys() const); EXTENSION(INLINE PyObject *get_tag_keys() const);
EXTENSION(PyObject *get_python_tags()); EXTENSION(PyObject *get_python_tags());

View File

@ -109,6 +109,8 @@ PUBLISHED:
INLINE int get_override(TypeHandle type) const; INLINE int get_override(TypeHandle type) const;
INLINE int get_override(int slot) const; INLINE int get_override(int slot) const;
MAKE_MAP_PROPERTY(attribs, has_attrib, get_attrib);
INLINE CPT(RenderState) get_unique() const; INLINE CPT(RenderState) get_unique() const;
virtual bool unref() const; virtual bool unref() const;

View File

@ -417,6 +417,14 @@ ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority) :
{ {
} }
/**
*
*/
INLINE ShaderInput::
operator bool () const {
return _type != M_invalid;
}
/** /**
* *
*/ */

View File

@ -104,6 +104,7 @@ PUBLISHED:
M_buffer, M_buffer,
}; };
INLINE operator bool() const;
INLINE bool operator == (const ShaderInput &other) const; INLINE bool operator == (const ShaderInput &other) const;
INLINE bool operator != (const ShaderInput &other) const; INLINE bool operator != (const ShaderInput &other) const;
INLINE bool operator < (const ShaderInput &other) const; INLINE bool operator < (const ShaderInput &other) const;

View File

@ -64,12 +64,18 @@ PUBLISHED:
int find_on_stage(const TextureStage *stage) const; int find_on_stage(const TextureStage *stage) const;
MAKE_SEQ_PROPERTY(on_stages, get_num_on_stages, get_on_stage);
MAKE_MAP_PROPERTY(on_stages, find_on_stage, get_on_stage);
INLINE int get_num_off_stages() const; INLINE int get_num_off_stages() const;
INLINE TextureStage *get_off_stage(int n) const; INLINE TextureStage *get_off_stage(int n) const;
MAKE_SEQ(get_off_stages, get_num_off_stages, get_off_stage); MAKE_SEQ(get_off_stages, get_num_off_stages, get_off_stage);
INLINE bool has_off_stage(TextureStage *stage) const; INLINE bool has_off_stage(TextureStage *stage) const;
INLINE bool has_all_off() const; INLINE bool has_all_off() const;
MAKE_SEQ_PROPERTY(off_stages, get_num_off_stages, get_off_stage);
MAKE_MAP_PROPERTY(off_stages, has_off_stage, get_off_stage);
INLINE bool is_identity() const; INLINE bool is_identity() const;
CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex, int override = 0) const; CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex, int override = 0) const;