Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ jobs:
cppwg src/ \
--wrapper_root wrapper/ \
--package_info wrapper/package_info.yaml \
--includes src/*/
--includes src/*/ \
--std c++17

- name: Build Python module
run: |
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ cd examples/shapes
cppwg src/ \
--wrapper_root wrapper/ \
--package_info wrapper/package_info.yaml \
--includes src/geometry/ src/math_funcs/ src/primitives/
--includes src/geometry/ src/math_funcs/ src/primitives/ \
--std c++17
```

To build the example package do:
Expand Down
28 changes: 21 additions & 7 deletions cppwg/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import argparse
import logging

from cppwg import CppWrapperGenerator
from cppwg import CppWrapperGenerator, __version__


def parse_args() -> argparse.Namespace:
Expand Down Expand Up @@ -44,11 +44,13 @@ def parse_args() -> argparse.Namespace:
help="Path to the castxml executable.",
)

# Note: we're passing in std directly because syntax like
# --castxml_cflags "-std=c++17" isn't supported by argparse because of
# the initial "-" in the argument. See https://bugs.python.org/issue9334
parser.add_argument(
"-f",
"--castxml_cflags",
"--std",
type=str,
help="Extra cflags for CastXML e.g. '-std=c++17'.",
help="C++ standard e.g. c++17.",
)

parser.add_argument(
Expand All @@ -63,7 +65,15 @@ def parse_args() -> argparse.Namespace:
"-q",
"--quiet",
action="store_true",
help="Disable info messages.",
help="Disable informational messages.",
)

parser.add_argument(
"-v",
"--version",
action="version",
version=__version__,
help="Print cppwg version.",
)

args = parser.parse_args()
Expand All @@ -80,13 +90,17 @@ def generate(args: argparse.Namespace) -> None:
args : argparse.Namespace
The parsed command line arguments.
"""
castxml_cflags = None
if args.std:
castxml_cflags = f"-std={args.std}"

generator = CppWrapperGenerator(
source_root=args.source_root,
source_includes=args.includes,
wrapper_root=args.wrapper_root,
package_info_path=args.package_info,
castxml_binary=args.castxml_binary,
castxml_cflags=args.castxml_cflags,
castxml_cflags=castxml_cflags,
)

generator.generate_wrapper()
Expand All @@ -103,7 +117,7 @@ def main() -> None:
logger = logging.getLogger()

if args.quiet:
logger.setLevel(logging.WARNING)
logger.setLevel(logging.ERROR)
else:
logger.setLevel(logging.INFO)

Expand Down
24 changes: 11 additions & 13 deletions cppwg/input/info_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def extract_templates_from_source(self, feature_info: BaseInfo) -> None:
raise FileNotFoundError()

# Get list of template substitutions from this feature and its parents
# e.g. {"signature":"<unsigned DIM,unsigned DIM>","replacement":[[2,2], [3,3]]}
# e.g. {"signature":"<unsigned DIM_A,unsigned DIM_B>","replacement":[[2,2], [3,3]]}
template_substitutions: List[Dict[str, Any]] = (
feature_info.hierarchy_attribute_gather("template_substitutions")
)
Expand All @@ -77,8 +77,8 @@ def extract_templates_from_source(self, feature_info: BaseInfo) -> None:
if len(template_substitutions) == 0:
return

# Remove spaces from template substitution signatures
# e.g. <unsigned DIM, unsigned DIM> -> <unsignedDIM,unsignedDIM>
# Remove spaces from template signatures
# e.g. <unsigned DIM_A, unsigned DIM_B> -> <unsignedDIM_A,unsignedDIM_B>
for tpl_sub in template_substitutions:
tpl_sub["signature"] = tpl_sub["signature"].replace(" ", "")

Expand All @@ -94,7 +94,7 @@ def extract_templates_from_source(self, feature_info: BaseInfo) -> None:
next_line = lines[idx + 1]

for template_substitution in template_substitutions:
# e.g. template<unsignedDIM,unsignedDIM>
# e.g. template<unsignedDIM_A,unsignedDIM_B>
signature: str = "template" + template_substitution["signature"]

# e.g. [[2,2], [3,3]]
Expand All @@ -106,34 +106,32 @@ def extract_templates_from_source(self, feature_info: BaseInfo) -> None:
declaration_found = False

if feature_string == next_line:
# template<unsignedDIM,unsignedDIM>
# template<unsignedDIM_A,unsignedDIM_B>
# classFoo
declaration_found = True

elif next_line.startswith(feature_string + "{"):
# template<unsignedDIM,unsignedDIM>
# template<unsignedDIM_A,unsignedDIM_B>
# classFoo{
declaration_found = True

elif next_line.startswith(feature_string + ":"):
# template<unsignedDIM,unsignedDIM>
# classFoo:publicBar<DIM,DIM>
# template<unsignedDIM_A,unsignedDIM_B>
# classFoo:publicBar<DIM_A,DIM_B>
declaration_found = True

elif curr_line == signature + feature_string:
# template<unsignedDIM,unsignedDIM>classFoo
# template<unsignedDIM_A,unsignedDIM_B>classFoo
declaration_found = True

elif curr_line.startswith(signature + feature_string + "{"):
# template<unsignedDIM,unsignedDIM>classFoo{
# template<unsignedDIM_A,unsignedDIM_B>classFoo{
declaration_found = True

elif curr_line.startswith(signature + feature_string + ":"):
# template<unsignedDIM,unsignedDIM>classFoo:publicBar<DIM,DIM>
# template<unsignedDIM_A,unsignedDIM_B>classFoo:publicBar<DIM_A,DIM_B>
declaration_found = True

# TODO: Add support for more cases, or find a better way e.g. regex or castxml?

if declaration_found:
feature_info.template_arg_lists = replacement
break
27 changes: 15 additions & 12 deletions cppwg/templates/pybind11_default.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,45 @@
from cppwg.utils.constants import CPPWG_CLASS_OVERRIDE_SUFFIX, CPPWG_EXT

class_cpp_header = """\
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
{includes}
#include "{class_short_name}.cppwg.hpp"
#include "{class_short_name}.%s.hpp"

namespace py = pybind11;
typedef {class_full_name} {class_short_name};
{smart_ptr_handle};
"""
""" % CPPWG_EXT

class_cpp_header_chaste = """\
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
{includes}
//#include "PythonObjectConverters.hpp"
#include "{class_short_name}.cppwg.hpp"
#include "{class_short_name}.%s.hpp"

namespace py = pybind11;
//PYBIND11_CVECTOR_TYPECASTER2();
//PYBIND11_CVECTOR_TYPECASTER3();
typedef {class_full_name} {class_short_name};
{smart_ptr_handle};
"""
""" % CPPWG_EXT

class_hpp_header = """\
#ifndef {class_short_name}_hpp__pyplusplus_wrapper
#define {class_short_name}_hpp__pyplusplus_wrapper
#ifndef {class_short_name}_hpp__%s_wrapper
#define {class_short_name}_hpp__%s_wrapper

namespace py = pybind11;
void register_{class_short_name}_class(py::module &m);
#endif // {class_short_name}_hpp__pyplusplus_wrapper
"""
#include <pybind11/pybind11.h>

void register_{class_short_name}_class(pybind11::module &m);
#endif // {class_short_name}_hpp__%s_wrapper
""" % tuple([CPPWG_EXT]*3)

class_virtual_override_header = """\
class {class_short_name}_Overloads : public {class_short_name}{{
class {class_short_name}%s : public {class_short_name}{{
public:
using {class_short_name}::{class_base_name};
"""
""" % CPPWG_CLASS_OVERRIDE_SUFFIX

class_virtual_override_footer = "}\n"

Expand Down
2 changes: 2 additions & 0 deletions cppwg/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
CPPWG_FALSE_STRINGS = ["OFF", "NO", "N", "FALSE", "F"]

CPPWG_DEFAULT_WRAPPER_DIR = "cppwg_wrappers"

CPPWG_CLASS_OVERRIDE_SUFFIX = "_Overrides"
38 changes: 1 addition & 37 deletions cppwg/writers/base_writer.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
"""Base for wrapper code writers."""

from collections import OrderedDict
from typing import Dict, List

from pygccxml.declarations import free_function_t
from typing import Dict


class CppBaseWrapperWriter:
Expand Down Expand Up @@ -56,40 +54,6 @@ def tidy_name(self, name: str) -> str:

return name

# TODO: Consider moving this implementation of exclusion_criteria to the
# free function writer it is only used there. exclusion_criteria is
# currently overriden in method writer and constructor writer.
def exclusion_criteria(
self, decl: free_function_t, exclusion_args: List[str]
) -> bool:
"""
Check if the function should be excluded from the wrapper code.

Parameters
----------
decl : free_function_t
The declaration of the function or class
exclusion_args : List[str]
A list of arguments to exclude from the wrapper code

Returns
-------
bool
True if the function should be excluded from the wrapper code
"""
# Check if any return types are not wrappable
return_type = decl.return_type.decl_string.replace(" ", "")
if return_type in exclusion_args:
return True

# Check if any arguments not wrappable
for decl_arg_type in decl.argument_types:
arg_type = decl_arg_type.decl_string.split()[0].replace(" ", "")
if arg_type in exclusion_args:
return True

return False

# TODO: This method is currently a placeholder. Consider implementing or removing.
def default_arg_exclusion_criteria(self) -> bool:
"""
Expand Down
33 changes: 19 additions & 14 deletions cppwg/writers/class_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
from pygccxml.declarations.class_declaration import class_t

from cppwg.input.class_info import CppClassInfo
from cppwg.utils.constants import CPPWG_EXT, CPPWG_HEADER_COLLECTION_FILENAME
from cppwg.utils.constants import (
CPPWG_CLASS_OVERRIDE_SUFFIX,
CPPWG_EXT,
CPPWG_HEADER_COLLECTION_FILENAME,
)
from cppwg.writers.base_writer import CppBaseWrapperWriter
from cppwg.writers.constructor_writer import CppConstructorWrapperWriter
from cppwg.writers.method_writer import CppMethodWrapperWriter
Expand Down Expand Up @@ -204,7 +208,7 @@ def add_virtual_overrides(
# Override virtual methods
if methods_needing_override:
# Add virtual override class, e.g.:
# class Foo_Overloads : public Foo{{
# class Foo_Overrides : public Foo {
# public:
# using Foo::Foo;
override_header_dict = {
Expand All @@ -217,8 +221,13 @@ def add_virtual_overrides(
].format(**override_header_dict)

# Override each method, e.g.:
# void bar(int a, bool b) override {{
# PYBIND11_OVERRIDE(void, Foo, bar, a, b);
# void bar(double d) const override {
# PYBIND11_OVERRIDE_PURE(
# bar,
# Foo2_2,
# bar,
# d);
# }
for method in methods_needing_override:
method_writer = CppMethodWrapperWriter(
self.class_info,
Expand All @@ -227,8 +236,7 @@ def add_virtual_overrides(
self.wrapper_templates,
short_class_name,
)
# TODO: Consider returning the override string instead
self.cpp_string = method_writer.add_override(self.cpp_string)
self.cpp_string += method_writer.generate_virtual_override_wrapper()

self.cpp_string += "\n};\n"

Expand Down Expand Up @@ -293,13 +301,12 @@ def write(self, work_dir: str) -> None:
self.add_virtual_overrides(class_decl, short_name)
)

# Add the virtual "trampoline" overrides from "Foo_Overloads" to
# Add the virtual "trampoline" overrides from "Foo_Overrides" to
# the "Foo" wrapper class definition if needed
# e.g. py::class_<Foo, Foo_Overloads >(m, "Foo")
# e.g. py::class_<Foo, Foo_Overrides >(m, "Foo")
overrides_string = ""
if methods_needing_override:
# TODO: Assign the "_Overloads" literal to a constant
overrides_string = f", {short_name}_Overloads"
overrides_string = f", {short_name}{CPPWG_CLASS_OVERRIDE_SUFFIX}"

# Add smart pointer support to the wrapper class definition if needed
# e.g. py::class_<Foo, boost::shared_ptr<Foo > >(m, "Foo")
Expand Down Expand Up @@ -344,8 +351,7 @@ def write(self, work_dir: str) -> None:
self.wrapper_templates,
short_name,
)
# TODO: Consider returning the constructor string instead
self.cpp_string = constructor_writer.add_self(self.cpp_string)
self.cpp_string += constructor_writer.generate_wrapper()

# Add public member functions
query = declarations.access_type_matcher_t("public")
Expand All @@ -364,8 +370,7 @@ def write(self, work_dir: str) -> None:
self.wrapper_templates,
short_name,
)
# TODO: Consider returning the member string instead
self.cpp_string = method_writer.add_self(self.cpp_string)
self.cpp_string += method_writer.generate_wrapper()

# Run any custom generators to add additional class code
if self.class_info.custom_generator:
Expand Down
Loading