Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
wjakob committed Feb 8, 2022
0 parents commit 8fd58b5
Show file tree
Hide file tree
Showing 14 changed files with 744 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "ext/robin_map"]
path = ext/robin_map
url = https://github.com/Tessil/robin-map
56 changes: 56 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
cmake_minimum_required(VERSION 3.17...3.22)
project(nanobind)

find_package(Python COMPONENTS Interpreter Development)

add_library(nanobind-core STATIC
src/module.cpp
src/func.cpp
)
target_compile_features(nanobind-core INTERFACE cxx_std_17)
set_target_properties(nanobind-core PROPERTIES
CXX_VISIBILITY_PRESET hidden
POSITION_INDEPENDENT_CODE ON
)
target_compile_features(nanobind-core PRIVATE cxx_std_17)
target_include_directories(nanobind-core PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${Python_INCLUDE_DIRS})

function(nanobind_add_module name)
if (MSVC)
set(NB_OPT_SIZE /Os)
else()
set(NB_OPT_SIZE -Os)
endif()

set(NB_SUFFIX ".so")
set(NB_COMPILE_OPTIONS
$<$<CONFIG:Release>:${NB_OPT_SIZE}>
$<$<CONFIG:MinSizeRel>:${NB_OPT_SIZE}>
$<$<CONFIG:RelWithDebInfo>:${NB_OPT_SIZE}>)

if(MSVC)
set(NB_COMPILE_OPTIONS ${NB_COMPILE_OPTIONS} /bigobj /MP)
set(NB_SUFFIX ".pyd")
endif()

add_library(${name} MODULE ${ARGV1})
set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden)
target_compile_features(${name} PRIVATE cxx_std_17)
target_compile_options(${name} PRIVATE ${NB_COMPILE_OPTIONS})
target_link_libraries(${name} nanobind-core)
set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX ".${Python_SOABI}${NB_SUFFIX}")
target_include_directories(${name} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${Python_INCLUDE_DIRS})

if (APPLE)
target_link_options(${name} PRIVATE -undefined dynamic_lookup)
endif()
endfunction()

nanobind_add_module(
nbtest
nbtest.cpp
)
1 change: 1 addition & 0 deletions ext/robin_map
Submodule robin_map added at a60341
14 changes: 14 additions & 0 deletions include/nanobind/nanobind.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#if __cplusplus < 201703L
# error NanoBind requires C++17
#endif

#include <stdexcept>
#include <type_traits>
#include <new>

#include "nb_python.h"
#include "nb_defs.h"
#include "nb_lib.h"
#include "nb_types.h"
#include "nb_attr.h"
#include "nb_func.h"
38 changes: 38 additions & 0 deletions include/nanobind/nb_attr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
NAMESPACE_BEGIN(nanobind)

struct scope {
handle value;
scope(handle value) : value(value) { }
};

struct pred {
handle value;
pred(handle value) : value(value) { }
};

struct name {
const char *value;
name(const char *value) : value(value) { }
};

NAMESPACE_BEGIN(detail)

inline void func_apply(void *func_rec, const pred &pred) {
func_set_pred(func_rec, pred.value.ptr());
}

inline void func_apply(void *func_rec, const scope &scope) {
func_set_scope(func_rec, scope.value.ptr());
}

inline void func_apply(void *func_rec, const name &name) {
func_set_name(func_rec, name.value);
}

inline void func_apply(void *func_rec, const char *docstr) {
func_set_docstr(func_rec, docstr);
}

NAMESPACE_END(detail)

NAMESPACE_END(nanobind)
38 changes: 38 additions & 0 deletions include/nanobind/nb_defs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#define NB_STRINGIFY(x) #x
#define NB_TOSTRING(x) NB_STRINGIFY(x)
#define NB_CONCAT(first, second) first##second

#if !defined(NAMESPACE_BEGIN)
# define NAMESPACE_BEGIN(name) namespace name {
#endif

#if !defined(NAMESPACE_END)
# define NAMESPACE_END(name) }
#endif

#if !defined(NB_EXPORT)
# if defined(_WIN32)
# define NB_EXPORT __declspec(dllexport)
# else
# define NB_EXPORT __attribute__ ((visibility("default")))
# endif
#endif

#define NB_MODULE(name, variable) \
extern "C" [[maybe_unused]] NB_EXPORT PyObject *PyInit_##name(); \
static PyModuleDef NB_CONCAT(nanobind_module_def_, name); \
[[maybe_unused]] static void NB_CONCAT(nanobind_init_, \
name)(::nanobind::module_ &); \
extern "C" NB_EXPORT PyObject *PyInit_##name() { \
nanobind::module_ m = nanobind::reinterpret_borrow<nanobind::module_>( \
nanobind::detail::module_new( \
NB_TOSTRING(name), &NB_CONCAT(nanobind_module_def_, name))); \
try { \
NB_CONCAT(nanobind_init_, name)(m); \
return m.ptr(); \
} catch (const std::exception &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
} \
} \
void NB_CONCAT(nanobind_init_, name)(::nanobind::module_ & (variable))
112 changes: 112 additions & 0 deletions include/nanobind/nb_func.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
NAMESPACE_BEGIN(nanobind)
NAMESPACE_BEGIN(detail)

template <typename Func, typename Return, typename... Args, typename... Extra>
object func_create(Func &&f, Return (*)(Args...), const Extra &...extra) {
struct capture {
std::remove_reference_t<Func> f;
};

// Store the capture object in the function record if there is space
constexpr bool IsSmall = sizeof(capture) <= sizeof(void *) * 3;
constexpr bool IsTrivial = std::is_trivially_destructible_v<capture>;

void *func_rec = func_alloc();
void (*free_capture)(void *ptr) = nullptr;

if constexpr (IsSmall) {
capture *cap = std::launder((capture *) func_rec);
new (cap) capture{ std::forward<Func>(f) };

if constexpr (!IsTrivial) {
free_capture = [](void *func_rec_2) {
capture *cap_2 = std::launder((capture *) func_rec_2);
cap_2->~capture();
};
}
} else {
void **cap = std::launder((void **) func_rec);
cap[0] = new capture{ std::forward<Func>(f) };

free_capture = [](void *func_rec_2) {
void **cap_2 = std::launder((void **) func_rec_2);
delete (capture *) cap_2[0];
};
}

auto impl = [](void *func_rec_2) -> PyObject * {
capture *cap;
if constexpr (IsSmall)
cap = std::launder((capture *) func_rec_2);
else
cap = std::launder((void **) func_rec_2)[0];

cap->f();

return nullptr;
};

(detail::func_apply(func_rec, extra), ...);

return reinterpret_steal<object>(func_init(func_rec, free_capture, impl));
}

template <typename T>
constexpr bool is_lambda_v = !std::is_function_v<T> && !std::is_pointer_v<T> &&
!std::is_member_pointer_v<T>;


/// Strip the class from a method type
template <typename T> struct remove_class { };
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { using type = R (A...); };
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...) const> { using type = R (A...); };

NAMESPACE_END(detail)

template <bool V> using enable_if_t = std::enable_if_t<V, int>;

template <typename Return, typename... Args, typename... Extra>
object cpp_function(Return (*f)(Args...), const Extra&... extra) {
return detail::func_create(f, f, extra...);
}

/// Construct a cpp_function from a lambda function (possibly with internal state)
template <typename Func, typename... Extra,
enable_if_t<detail::is_lambda_v<std::remove_reference_t<Func>>> = 0>
object cpp_function(Func &&f, const Extra &...extra) {
using RawFunc =
typename detail::remove_class<decltype(&Func::operator())>::type;
return detail::func_create(std::forward<Func>(f), (RawFunc *) nullptr,
extra...);
}

/// Construct a cpp_function from a class method (non-const, no ref-qualifier)
template <typename Return, typename Class, typename... Args, typename... Extra>
object cpp_function(Return (Class::*f)(Args...), const Extra&... extra) {
return detail::func_create(
[f](Class *c, Args... args) -> Return {
return (c->*f)(std::forward<Args>(args)...);
},
(Return(*)(Class *, Args...)) nullptr, extra...);
}

/// Construct a cpp_function from a class method (const, no ref-qualifier)
template <typename Return, typename Class, typename... Args, typename... Extra>
object cpp_function(Return (Class::*f)(Args...) const, const Extra &...extra) {
return detail::func_create(
[f](const Class *c, Args... args) -> Return {
return (c->*f)(std::forward<Args>(args)...);
},
(Return(*)(const Class *, Args...)) nullptr, extra...);
}

template <typename Func, typename... Extra>
module_ &module_::def(const char *name_, Func &&f, const Extra &...extra) {
object func = cpp_function(std::forward<Func>(f), name(name_), scope(*this),
pred(getattr(*this, name_, none())), extra...);
if (PyModule_AddObject(m_ptr, name_, func.release().ptr()))
detail::fail("module::def(): could not add object!");
return *this;
}

NAMESPACE_END(nanobind)
58 changes: 58 additions & 0 deletions include/nanobind/nb_lib.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
NAMESPACE_BEGIN(nanobind)
NAMESPACE_BEGIN(detail)

// ========================================================================

/// Raise a std::runtime_error with the given message
#if defined(__GNUC__)
__attribute__((noreturn, __format__ (__printf__, 1, 2)))
#else
[[noreturn]]
#endif
extern void raise(const char *fmt, ...);

/// Abort the process with a fatal error
#if defined(__GNUC__)
__attribute__((noreturn, nothrow, __format__ (__printf__, 1, 2)))
#else
[[noreturn, noexcept]]
#endif
extern void fail(const char *fmt, ...);

// ========================================================================

/// Create a new capsule object
extern PyObject *capsule_new(const void *ptr, void (*free)(void *)) noexcept;

/// Create a new extension module with the given name
extern PyObject *module_new(const char *name, PyModuleDef *def) noexcept;

// ========================================================================

/// Create a new handle for a function to be bound
extern void *func_alloc() noexcept;

/// Free all memory associated with a function handle
extern void func_free(void *handle) noexcept;

/// Annotate a function handle with the given flag
extern void func_set_flag(void *handle, uint32_t flag) noexcept;

/// Set the function name
extern void func_set_name(void *handle, const char *name) noexcept;

/// Set the function docstring
extern void func_set_docstr(void *handle, const char *docstr) noexcept;

/// Set the function scope
extern void func_set_scope(void *handle, PyObject *scope) noexcept;

/// Set the predecessor of a overload chain
extern void func_set_pred(void *handle, PyObject *pred) noexcept;

/// Create a Python function object for the given function handle
extern PyObject *func_init(void *handle, void (*free_captured)(void *),
PyObject *(*impl)(void *)) noexcept;

NAMESPACE_END(detail)
NAMESPACE_END(nanobind)
39 changes: 39 additions & 0 deletions include/nanobind/nb_python.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
#if defined(_MSC_VER)
# pragma warning(push)
// C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only)
# pragma warning(disable: 4505)
# if defined(_DEBUG) && !defined(Py_DEBUG)
# define PYBIND11_DEBUG_MARKER
# undef _DEBUG
# endif
#endif

#include <Python.h>
#include <frameobject.h>
#include <pythread.h>

/* Python #defines overrides on all sorts of core functions, which
tends to weak havok in C++ codebases that expect these to work
like regular functions (potentially with several overloads) */
#if defined(isalnum)
# undef isalnum
# undef isalpha
# undef islower
# undef isspace
# undef isupper
# undef tolower
# undef toupper
#endif

#if defined(copysign)
# undef copysign
#endif

#if defined(_MSC_VER)
# if defined(PYBIND11_DEBUG_MARKER)
# define _DEBUG
# undef PYBIND11_DEBUG_MARKER
# endif
# pragma warning(pop)
#endif
Loading

0 comments on commit 8fd58b5

Please sign in to comment.