Skip to content

Commit

Permalink
implement struct overloading for python
Browse files Browse the repository at this point in the history
Implements the overloaded struct example for the Python wrapper.
  • Loading branch information
goatshriek authored Oct 2, 2024
1 parent 25867f1 commit a715933
Show file tree
Hide file tree
Showing 20 changed files with 398 additions and 162 deletions.
10 changes: 9 additions & 1 deletion docs/examples/overloaded_struct/security_system.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0

/*
* Copyright 2019 Joel E. Anderson
* Copyright 2019-2024 Joel E. Anderson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,6 +23,10 @@
#define GLASS_BREAK_EVENT 2
#define CAMERA_EVENT 3

#ifdef __cplusplus
extern "C" {
#endif

struct event {
int code;
void *data;
Expand Down Expand Up @@ -61,4 +65,8 @@ print_glass_break_event( const struct event *ev );
void
print_motion_event( const struct event *ev );

#ifdef __cplusplus
}
#endif

#endif
17 changes: 10 additions & 7 deletions docs/examples/overloaded_struct/security_system.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
version: "0.3.0"
classes:
- name: "SecurityEvent"
namespace: "security_system"
namespace: "home_automation"
libraries: "security_system"
equivalent-struct:
name: "event"
includes: "security_system.h"
Expand Down Expand Up @@ -33,6 +34,8 @@ classes:
wrapped-function:
name: "get_next_event"
includes: "security_system.h"
return:
type: "equivalent-struct-pointer"
- name: "Print"
virtual: true
wrapped-function:
Expand All @@ -41,10 +44,10 @@ classes:
params:
- value: "equivalent-struct-pointer"
- name: "CameraEvent"
namespace: "security_system"
namespace: "home_automation"
libraries: "security_system"
parent:
name: "SecurityEvent"
includes: "SecurityEvent.hpp"
equivalent-struct:
name: "event"
includes: "security_system.h"
Expand All @@ -68,10 +71,10 @@ classes:
params:
- value: "equivalent-struct-pointer"
- name: "GlassBreakEvent"
namespace: "security_system"
namespace: "home_automation"
libraries: "security_system"
parent:
name: "SecurityEvent"
includes: "SecurityEvent.hpp"
equivalent-struct:
name: "event"
includes: "security_system.h"
Expand All @@ -95,10 +98,10 @@ classes:
params:
- value: "equivalent-struct-pointer"
- name: "MotionEvent"
namespace: "security_system"
namespace: "home_automation"
libraries: "security_system"
parent:
name: "SecurityEvent"
includes: "SecurityEvent.hpp"
equivalent-struct:
name: "event"
includes: "security_system.h"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0

/*
* Copyright 2019 Joel E. Anderson
* Copyright 2019-2024 Joel E. Anderson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,7 @@
#include <cstdlib>
#include <SecurityEvent.hpp>

using namespace security_system;
using namespace home_automation;

int
main( int argc, char **argv ) {
Expand Down
22 changes: 22 additions & 0 deletions docs/examples/overloaded_struct/security_system_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env python

# SPDX-License-Identifier: Apache-2.0

# Copyright 2024 Joel E. Anderson
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import home_automation

for i in range(5):
home_automation.SecurityEvent.NextEvent().Print()
28 changes: 19 additions & 9 deletions lib/wrapture/class_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ def child?
@spec.key?('parent')
end

# A list of constructor functions for the class.
def constructors
@functions.select(&:constructor?)
end

# A list of includes needed for the declaration of the class.
def declaration_includes
includes = @spec['includes'].dup
Expand Down Expand Up @@ -200,11 +205,14 @@ def definition_includes
includes.concat(const.definition_includes)
end

includes.concat(factory_definition_includes)

includes.uniq
end

# The destructor function for the class, or nil if there isn't one.
def destructor
@functions.select(&:destructor?).first
end

# Calls the given block for each line of the class documentation.
def documentation(&block)
@doc&.format_as_doxygen(max_line_length: 78) { |line| block.call(line) }
Expand Down Expand Up @@ -237,6 +245,15 @@ def libraries
@functions.flat_map(&:libraries).concat(@spec['libraries'])
end

# An array of methods of the class. This is a subset of the list of
# functions without the constructors and destructors.
#
# Named with a specs suffix to avoid conflicts with Ruby's "methods"
# instance method.
def method_specs
@functions.select { |spec| !spec.constructor? && !spec.destructor? }
end

# The name of the class.
def name
@spec['name']
Expand Down Expand Up @@ -301,12 +318,5 @@ def type(type)
def type?(type)
@scope.type?(type)
end

private

# A list of the includes needed for the factory function definition.
def factory_definition_includes
@scope.overloads(self).map { |overload| "#{overload.name}.hpp" }
end
end
end
70 changes: 62 additions & 8 deletions lib/wrapture/cpp_wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,8 @@ def declare_class
# Gives each line of the declaration of the given ConstantSpec.
def declare_constant(constant_spec, &block)
constant_spec.doc&.format_as_doxygen(max_line_length: 76, &block)
yield "static const #{constant_spec.type.variable(constant_spec.name)};"
variable = type_variable(constant_spec.type, constant_spec.name)
yield "static const #{variable};"
end

# Gives each line of the declaration of a FunctionSpec to the provided
Expand Down Expand Up @@ -543,7 +544,14 @@ def define_function

# A list of includes needed for the definition of the class.
def definition_includes
@spec.definition_includes.concat(common_includes(@spec))
includes = @spec.definition_includes
includes.concat(common_includes(@spec))

@spec.scope.overloads(@spec).map do |overload|
includes.append("#{overload.name}.hpp")
end

includes
end

# The definition of an enum element.
Expand Down Expand Up @@ -577,6 +585,9 @@ def equivalent_member_field(field_name)
end

# A spec hash for a factory constructor for this class.
#
# A factory constructor creates an instance of a class based on a struct
# that is overloaded.
def factory_constructor_hash
factory_lines = []
line_prefix = ''
Expand Down Expand Up @@ -605,7 +616,7 @@ def function_declaration_param_list(func_spec)
'void'
else
func_spec.params.map do |param|
sig = param.type.resolve(func_spec).variable(param.name)
sig = type_variable(param.type.resolve(func_spec), param.name)

if param.default_value?
sig += ' = '
Expand All @@ -629,15 +640,15 @@ def function_declaration_signature(func_spec)
"#{func_spec.name}( #{function_declaration_param_list(func_spec)} )"
else
return_type = func_spec.resolved_return
return_type.return_expression(func_spec, func_name: func_spec.name)
return_expression(return_type, func_spec, func_name: func_spec.name)
end
end

# The parameter list for the function definition.
def function_definition_param_list(func_spec)
if func_spec.params?
func_spec.params.map do |param|
param.type.resolve(func_spec).variable(param.name)
type_variable(param.type.resolve(func_spec), param.name)
end.join(', ')
else
'void'
Expand All @@ -651,7 +662,7 @@ def function_definition_signature(func_spec)
"#{func_name}( #{function_definition_param_list(func_spec)} )"
else
return_type = func_spec.resolved_return
return_type.return_expression(func_spec, func_name: func_name)
return_expression(return_type, func_spec, func_name: func_name)
end
end

Expand All @@ -661,7 +672,7 @@ def function_locals(spec)

if spec.capture_return?
wrapped_type = spec.resolve_type(spec.wrapped.return_val_type)
yield "#{wrapped_type.variable('return_val')};"
yield "#{type_variable(wrapped_type, 'return_val')};"
end
end

Expand Down Expand Up @@ -735,8 +746,34 @@ def return_cast(value)
elsif @spec.return_overloaded?
"new#{@spec.return_type.name.chomp('*').strip} ( #{value} )"
else
@spec.resolved_return.cast_expression(value)
return_type = @spec.resolved_return
"( #{type_variable(return_type)} )( #{value} )"
end
end

# A string with a declaration of FunctionSpec +func+ with the given type as
# the return value. +func_name+ can be provided to override the function
# name, for example if a class name needs to be included.
def return_expression(type_spec, func_spec, func_name: func_spec.name)
name_part = String.new(func_name || '')
param_part = String.new
ret_part = String.new(type_spec.name || '')

current_type = type_spec
while current_type.function?
name_part.prepend('( *')

current_func = current_type.function
param_list = function_definition_param_list(current_func)
param_part.concat(" )( #{param_list} )")

current_type = current_func.resolved_return
ret_part = current_type.name
end

ret_part << ' ' unless current_type.pointer?
param_list = function_definition_param_list(func_spec)
"#{ret_part}#{name_part}( #{param_list} )#{param_part}"
end

# The return statement used in this function's definition.
Expand Down Expand Up @@ -777,6 +814,23 @@ def this_struct_pointer
"#{'&' unless @spec.owner.pointer_wrapper?}this->equivalent"
end

# A string with a declaration of a variable named +var_name+ of this type.
# If +var_name+ is nil then this will simply be a type declaration.
def type_variable(type_spec, var_name = nil)
if type_spec.variadic?
'...'
elsif type_spec.function?
func_spec = type_spec.function
func_name = "( *#{var_name} )"
return_expression(func_spec.resolved_return, func_spec,
func_name: func_name)
elsif var_name.nil?
type_spec.name
else
"#{type_spec.name}#{' ' unless type_spec.pointer?}#{var_name}"
end
end

# The expression containing the call to the underlying wrapped function.
def wrapped_call_expression
call = @spec.wrapped.call_from(self)
Expand Down
12 changes: 3 additions & 9 deletions lib/wrapture/function_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,9 @@ def optional_params
end

# A string with the parameter list for this function.
def param_list
ParamSpec.signature(@params, self)
end
# def param_list
# ParamSpec.signature(@params, self)
# end

# An array of the names of the function params.
def param_names
Expand Down Expand Up @@ -318,12 +318,6 @@ def returns_call_directly?
!@wrapped.error_check?
end

# The signature of the function. +func_name+ can be used to override the
# function name if needed, for example if a class name qualifier is needed.
def signature(func_name: name)
"#{func_name}( #{param_list} )"
end

# True if the function is static.
def static?
@spec['static']
Expand Down
36 changes: 18 additions & 18 deletions lib/wrapture/param_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,24 +130,24 @@ def name
# The parameter type and name, suitable for use in a function signature or
# declaration. +owner+ must be the FunctionSpec that the parameter belongs
# to.
def signature(owner)
sig = @type.resolve(owner).variable(name)

if @spec.key?('default-value')
default_value = @spec['default-value']

sig += ' = '
sig += if @spec['type'] == 'const char *'
"\"#{default_value}\""
elsif @spec['type'].end_with?('char')
"'#{default_value}'"
else
default_value.to_s
end
end

sig
end
# def signature(owner)
# sig = @type.resolve(owner).variable(name)

# if @spec.key?('default-value')
# default_value = @spec['default-value']

# sig += ' = '
# sig += if @spec['type'] == 'const char *'
# "\"#{default_value}\""
# elsif @spec['type'].end_with?('char')
# "'#{default_value}'"
# else
# default_value.to_s
# end
# end

# sig
# end

# True if this parameter is variadic (the name is equal to '...').
def variadic?
Expand Down
Loading

0 comments on commit a715933

Please sign in to comment.