first commit

This commit is contained in:
maxbyte9p
2024-05-13 11:48:08 -04:00
commit f4baf557d7
19 changed files with 4697 additions and 0 deletions

23
CMakeLists.txt Normal file
View File

@@ -0,0 +1,23 @@
#set (CMAKE_CXX_STANDARD 20)
include_directories(${CMAKE_SOURCE_DIR}/include)
include_directories(/usr/include/julia)
list( APPEND CMAKE_INSTALL_RPATH /usr/lib64 /usr/lib64/julia )
add_library(hello SHARED hello.cpp)
target_link_libraries(hello ${JLCXX_TARGET} ${JLCXX_STL_TARGET} ${Julia_LIBRARY})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu11 -I'/usr/include/julia' -fPIC -L'/usr/lib64' -Wl,--export-dynamic -Wl,-rpath,'/usr/lib64' -Wl,-rpath,'/usr/lib64/julia' -ljulia")
add_executable(main main.cpp)
target_link_libraries(main ${Julia_LIBRARY})
install(TARGETS
#hello
main
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libcxxjl_test)

182
FindJulia.cmake Normal file
View File

@@ -0,0 +1,182 @@
# Original FindJulia.cmake from https://github.com/QuantStack/xtensor-julia-cookiecutter/blob/master/%7B%7Bcookiecutter.github_project_name%7D%7D/cmake/FindJulia.cmake
if(Julia_FOUND)
return()
endif()
####################
# Julia Executable #
####################
if(Julia_PREFIX)
message(STATUS "Adding path ${Julia_PREFIX} to search path")
list(APPEND CMAKE_PREFIX_PATH ${Julia_PREFIX})
message(STATUS "THIS BRANCH")
else()
find_program(Julia_EXECUTABLE julia DOC "Julia executable")
message(STATUS "Found Julia executable: " ${Julia_EXECUTABLE})
endif()
#################
# Julia Version #
#################
if(Julia_EXECUTABLE)
execute_process(
COMMAND "${Julia_EXECUTABLE}" --startup-file=no --version
OUTPUT_VARIABLE Julia_VERSION_STRING
)
else()
find_file(Julia_VERSION_INCLUDE julia_version.h PATH_SUFFIXES include/julia)
file(READ ${Julia_VERSION_INCLUDE} Julia_VERSION_STRING)
string(REGEX MATCH "JULIA_VERSION_STRING.*" Julia_VERSION_STRING ${Julia_VERSION_STRING})
endif()
string(
REGEX REPLACE ".*([0-9]+\\.[0-9]+\\.[0-9]+).*" "\\1"
Julia_VERSION_STRING "${Julia_VERSION_STRING}"
)
MESSAGE(STATUS "Julia_VERSION_STRING: ${Julia_VERSION_STRING}")
##################
# Julia Includes #
##################
set(JULIA_HOME_NAME "Sys.BINDIR")
if(${Julia_VERSION_STRING} VERSION_LESS "0.7.0")
set(JULIA_HOME_NAME "JULIA_HOME")
else()
set(USING_LIBDL "using Libdl")
endif()
if(DEFINED ENV{JULIA_INCLUDE_DIRS})
set(Julia_INCLUDE_DIRS $ENV{JULIA_INCLUDE_DIRS}
CACHE STRING "Location of Julia include files")
elseif(Julia_EXECUTABLE)
execute_process(
COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "julia_include_dir = joinpath(match(r\"(.*)(bin)\",${JULIA_HOME_NAME}).captures[1],\"include\",\"julia\")\n
if !isdir(julia_include_dir) # then we're running directly from build\n
julia_base_dir_aux = splitdir(splitdir(${JULIA_HOME_NAME})[1])[1] # useful for running-from-build\n
julia_include_dir = joinpath(julia_base_dir_aux, \"usr\", \"include\" )\n
julia_include_dir *= \";\" * joinpath(julia_base_dir_aux, \"src\", \"support\" )\n
julia_include_dir *= \";\" * joinpath(julia_base_dir_aux, \"src\" )\n
end\n
julia_include_dir"
OUTPUT_VARIABLE Julia_INCLUDE_DIRS
)
string(REGEX REPLACE "\"" "" Julia_INCLUDE_DIRS "${Julia_INCLUDE_DIRS}")
string(REGEX REPLACE "\n" "" Julia_INCLUDE_DIRS "${Julia_INCLUDE_DIRS}")
set(Julia_INCLUDE_DIRS ${Julia_INCLUDE_DIRS}
CACHE PATH "Location of Julia include files")
elseif(Julia_PREFIX)
set(Julia_INCLUDE_DIRS ${Julia_PREFIX}/include/julia)
endif()
set(Julia_INCLUDE_DIRS ${Julia_INCLUDE_DIRS};$ENV{includedir})
MESSAGE(STATUS "Julia_INCLUDE_DIRS: ${Julia_INCLUDE_DIRS}")
###################
# Julia Libraries #
###################
if(WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES} .a;.dll)
endif()
if(Julia_EXECUTABLE)
execute_process(
COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "${USING_LIBDL}\nabspath(Libdl.dlpath((ccall(:jl_is_debugbuild, Cint, ()) != 0) ? \"libjulia-debug\" : \"libjulia\"))"
OUTPUT_VARIABLE Julia_LIBRARY
)
string(REGEX REPLACE "\"" "" Julia_LIBRARY "${Julia_LIBRARY}")
string(REGEX REPLACE "\n" "" Julia_LIBRARY "${Julia_LIBRARY}")
string(STRIP "${Julia_LIBRARY}" Julia_LIBRARY)
if(WIN32)
get_filename_component(Julia_LIBRARY_DIR ${Julia_LIBRARY} DIRECTORY)
get_filename_component(Julia_LIBRARY_DIR ${Julia_LIBRARY_DIR} DIRECTORY)
find_library(win_Julia_LIBRARY
NAMES libjulia.dll.a
PATHS "${Julia_LIBRARY_DIR}/lib"
NO_DEFAULT_PATH
)
set(Julia_LIBRARY "${win_Julia_LIBRARY}")
endif()
set(Julia_LIBRARY "${Julia_LIBRARY}"
CACHE PATH "Julia library")
else()
find_library(Julia_LIBRARY NAMES libjulia.${Julia_VERSION_STRING}.dylib julia libjulia.dll.a libjulia CMAKE_FIND_ROOT_PATH_BOTH)
endif()
get_filename_component(Julia_LIBRARY_DIR ${Julia_LIBRARY} DIRECTORY)
MESSAGE(STATUS "Julia_LIBRARY_DIR: ${Julia_LIBRARY_DIR}")
MESSAGE(STATUS "Julia_LIBRARY: ${Julia_LIBRARY}")
##############
# JULIA_HOME #
##############
if(Julia_EXECUTABLE)
execute_process(
COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "${JULIA_HOME_NAME}"
OUTPUT_VARIABLE JULIA_HOME
)
string(REGEX REPLACE "\"" "" JULIA_HOME "${JULIA_HOME}")
string(REGEX REPLACE "\n" "" JULIA_HOME "${JULIA_HOME}")
MESSAGE(STATUS "JULIA_HOME: ${JULIA_HOME}")
###################
# libLLVM version #
###################
execute_process(
COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "Base.libllvm_version"
OUTPUT_VARIABLE Julia_LLVM_VERSION
)
string(REGEX REPLACE "\"" "" Julia_LLVM_VERSION "${Julia_LLVM_VERSION}")
string(REGEX REPLACE "\n" "" Julia_LLVM_VERSION "${Julia_LLVM_VERSION}")
MESSAGE(STATUS "Julia_LLVM_VERSION: ${Julia_LLVM_VERSION}")
endif()
##################################
# Check for Existence of Headers #
##################################
find_path(Julia_MAIN_HEADER julia.h HINTS ${Julia_INCLUDE_DIRS})
#######################################
# Determine if we are on 32 or 64 bit #
#######################################
if(Julia_EXECUTABLE)
execute_process(
COMMAND ${Julia_EXECUTABLE} --startup-file=no -E "Sys.WORD_SIZE"
OUTPUT_VARIABLE Julia_WORD_SIZE
)
string(REGEX REPLACE "\n" "" Julia_WORD_SIZE "${Julia_WORD_SIZE}")
MESSAGE(STATUS "Julia_WORD_SIZE: ${Julia_WORD_SIZE}")
endif()
if($ENV{target} MATCHES "^i686.*")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse -msse2")
endif()
###########################
# FindPackage Boilerplate #
###########################
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Julia
REQUIRED_VARS Julia_LIBRARY Julia_LIBRARY_DIR Julia_INCLUDE_DIRS Julia_MAIN_HEADER
VERSION_VAR Julia_VERSION_STRING
FAIL_MESSAGE "Julia not found"
)

15
README.md Normal file
View File

@@ -0,0 +1,15 @@
# Julia CMake Integration
Make sure you install Julia on your Fedora workstation.
```
sudo dnf install -y julia
```
Make sure you have CMake and whatever unlisted deps because I probably forgot some required deps that need installed.
Also btw open the Julia REPL and install the CxxWrap package. Press "]" in the REPL then.
```
add CxxWrap
``
It should bitch if you're missing something it needs.

4
build.sh Normal file
View File

@@ -0,0 +1,4 @@
g++ -c -fPIC ../hello.cpp -I'../include/' -I'/usr/include/julia' -fPIC -L'/usr/lib64' -Wl,--export-dynamic -Wl,-rpath,'/usr/lib64' -Wl,-rpath,'/usr/lib64/julia' -ljulia
g++ hello.o -shared -o libhello.so
g++ ../main.cpp -o main -I'/usr/include/julia' -fPIC -L'/usr/lib64' -Wl,--export-dynamic -Wl,-rpath,'/usr/lib64' -Wl,-rpath,'/usr/lib64/julia' -ljulia

13
hello.cpp Normal file
View File

@@ -0,0 +1,13 @@
#include <string>
#include "jlcxx/jlcxx.hpp"
std::string greet()
{
return "hello, world";
}
JLCXX_MODULE define_julia_module(jlcxx::Module& mod)
{
mod.method("greet", &greet, "documentation for greet");
}

463
include/jlcxx/array.hpp Normal file
View File

@@ -0,0 +1,463 @@
#ifndef JLCXX_ARRAY_HPP
#define JLCXX_ARRAY_HPP
#include "type_conversion.hpp"
#include "tuple.hpp"
namespace jlcxx
{
/// wrapper for julia jl_array_data for different julia versions
template <typename T>
T* jlcxx_array_data(jl_array_t* arr) {
#if (JULIA_VERSION_MAJOR * 100 + JULIA_VERSION_MINOR) >= 111
return jl_array_data(arr, T);
#else
return static_cast<T*>(jl_array_data(arr));
#endif
}
template<typename PointedT, typename CppT>
struct ValueExtractor
{
inline CppT operator()(PointedT* p)
{
return convert_to_cpp<CppT>(*p);
}
};
template<typename PointedT>
struct ValueExtractor<PointedT, PointedT>
{
inline PointedT& operator()(PointedT* p)
{
return *p;
}
};
template<typename PointedT, typename CppT>
class array_iterator_base
{
private:
PointedT* m_ptr;
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = CppT;
using difference_type = ptrdiff_t;
using pointer = CppT*;
using reference = CppT&;
array_iterator_base() : m_ptr(nullptr)
{
}
explicit array_iterator_base(PointedT* p) : m_ptr(p)
{
}
template <class OtherPointedT, class OtherCppT>
array_iterator_base(array_iterator_base<OtherPointedT, OtherCppT> const& other) : m_ptr(other.m_ptr) {}
auto operator*() -> decltype(ValueExtractor<PointedT,CppT>()(m_ptr))
{
return ValueExtractor<PointedT,CppT>()(m_ptr);
}
array_iterator_base<PointedT, CppT>& operator++()
{
++m_ptr;
return *this;
}
array_iterator_base<PointedT, CppT> operator++(int)
{
auto result(*this);
++(*this);
return result;
}
array_iterator_base<PointedT, CppT>& operator--()
{
--m_ptr;
return *this;
}
array_iterator_base<PointedT, CppT> operator--(int)
{
auto result(*this);
--(*this);
return result;
}
array_iterator_base<PointedT, CppT>& operator+=(std::ptrdiff_t n)
{
m_ptr += n;
return *this;
}
array_iterator_base<PointedT, CppT>& operator-=(std::ptrdiff_t n)
{
m_ptr -= n;
return *this;
}
PointedT* ptr() const
{
return m_ptr;
}
};
/// Wrap a Julia 1D array in a C++ class. Array is allocated on the C++ side
template<typename ValueT>
class Array
{
public:
Array(const size_t n = 0)
{
jl_value_t* array_type = apply_array_type(julia_type<ValueT>(), 1);
m_array = jl_alloc_array_1d(array_type, n);
}
Array(jl_datatype_t* applied_type, const size_t n = 0)
{
jl_value_t* array_type = apply_array_type(applied_type, 1);
m_array = jl_alloc_array_1d(array_type, n);
}
/// Append an element to the end of the list
template<typename VT>
void push_back(VT&& val)
{
JL_GC_PUSH1(&m_array);
const size_t pos = jl_array_len(m_array);
jl_array_grow_end(m_array, 1);
jl_value_t* jval = box<ValueT>(val);
jl_array_ptr_set(m_array, pos, jval);
JL_GC_POP();
}
/// Access to the wrapped array
jl_array_t* wrapped()
{
return m_array;
}
// access to the pointer for GC macros
jl_array_t** gc_pointer()
{
return &m_array;
}
private:
jl_array_t* m_array;
};
namespace detail
{
template<typename T, typename TraitT=mapping_trait<T>>
struct ArrayElementType
{
using type = static_julia_type<T>;
};
template<typename T>
struct ArrayElementType<T,WrappedPtrTrait>
{
using type = T;
};
}
/// Reference a Julia array in an STL-compatible wrapper
template<typename ValueT, int Dim = 1>
class ArrayRef
{
public:
using julia_t = typename detail::ArrayElementType<ValueT>::type;
ArrayRef(jl_array_t* arr) : m_array(arr)
{
assert(wrapped() != nullptr);
}
/// Convert from existing C-array (memory owned by C++)
template<typename... SizesT>
ArrayRef(julia_t* ptr, const SizesT... sizes);
/// Convert from existing C-array, explicitly setting Julia ownership
template<typename... SizesT>
ArrayRef(const bool julia_owned, julia_t* ptr, const SizesT... sizes);
typedef array_iterator_base<julia_t, ValueT> iterator;
typedef array_iterator_base<julia_t const, ValueT const> const_iterator;
inline jl_array_t* wrapped() const
{
return m_array;
}
iterator begin()
{
return iterator(jlcxx_array_data<julia_t>(wrapped()));
}
const_iterator begin() const
{
return const_iterator(jlcxx_array_data<julia_t>(wrapped()));
}
iterator end()
{
return iterator(jlcxx_array_data<julia_t>(wrapped()) + jl_array_len(wrapped()));
}
const_iterator end() const
{
return const_iterator(jlcxx_array_data<julia_t>(wrapped()) + jl_array_len(wrapped()));
}
void push_back(const ValueT& val)
{
static_assert(Dim == 1, "ArrayRef::push_back is only for 1D ArrayRef");
static_assert(std::is_same<julia_t,ValueT>::value, "ArrayRef::push_back is only for arrays of fundamental types");
jl_array_t* arr_ptr = wrapped();
JL_GC_PUSH1(&arr_ptr);
const size_t pos = size();
jl_array_grow_end(arr_ptr, 1);
data()[pos] = val;
JL_GC_POP();
}
const julia_t* data() const
{
return jlcxx_array_data<julia_t>(wrapped());
}
julia_t* data()
{
return jlcxx_array_data<julia_t>(wrapped());
}
std::size_t size() const
{
return jl_array_len(wrapped());
}
ValueT& operator[](const std::size_t i)
{
if constexpr(std::is_same<julia_t, ValueT>::value)
{
return data()[i];
}
else
{
return *extract_pointer_nonull<ValueT>(data()[i]);
}
}
const ValueT& operator[](const std::size_t i) const
{
if constexpr(std::is_same<julia_t, ValueT>::value)
{
return data()[i];
}
else
{
return *extract_pointer_nonull<ValueT>(data()[i]);
}
}
jl_array_t* m_array;
};
// Conversions
template<typename T, int Dim, typename SubTraitT>
struct static_type_mapping<ArrayRef<T, Dim>, CxxWrappedTrait<SubTraitT>>
{
typedef jl_array_t* type;
};
namespace detail
{
template<typename T, typename TraitT=mapping_trait<T>>
struct PackedArrayType
{
static jl_datatype_t* type()
{
return julia_type<T>();
}
};
template<typename T>
struct PackedArrayType<T*, WrappedPtrTrait>
{
static jl_datatype_t* type()
{
return apply_type(jlcxx::julia_type("Ptr"), julia_base_type<T>());
}
};
template<typename T, typename SubTraitT>
struct PackedArrayType<T,CxxWrappedTrait<SubTraitT>>
{
static jl_datatype_t* type()
{
create_if_not_exists<T&>();
return julia_type<T&>();
}
};
}
template<typename T, int Dim>
struct julia_type_factory<ArrayRef<T, Dim>>
{
static inline jl_datatype_t* julia_type()
{
create_if_not_exists<T>();
return (jl_datatype_t*)apply_array_type(detail::PackedArrayType<T>::type(), Dim);
}
};
template<typename ValueT, typename... SizesT>
jl_array_t* wrap_array(const bool julia_owned, ValueT* c_ptr, const SizesT... sizes)
{
jl_datatype_t* dt = julia_type<ArrayRef<ValueT, sizeof...(SizesT)>>();
jl_value_t *dims = nullptr;
JL_GC_PUSH1(&dims);
dims = convert_to_julia(std::make_tuple(static_cast<cxxint_t>(sizes)...));
jl_array_t* result = jl_ptr_to_array((jl_value_t*)dt, c_ptr, dims, julia_owned);
JL_GC_POP();
return result;
}
template<typename ValueT, int Dim>
template<typename... SizesT>
ArrayRef<ValueT, Dim>::ArrayRef(julia_t* c_ptr, const SizesT... sizes) : m_array(wrap_array(false, c_ptr, sizes...))
{
}
template<typename ValueT, int Dim>
template<typename... SizesT>
ArrayRef<ValueT, Dim>::ArrayRef(const bool julia_owned, julia_t* c_ptr, const SizesT... sizes) : m_array(wrap_array(julia_owned, c_ptr, sizes...))
{
}
template<typename ValueT, typename... SizesT>
auto make_julia_array(ValueT* c_ptr, const SizesT... sizes) -> ArrayRef<ValueT, sizeof...(SizesT)>
{
return ArrayRef<ValueT, sizeof...(SizesT)>(false, c_ptr, sizes...);
}
template<typename T, typename SubTraitT>
struct static_type_mapping<Array<T>, CxxWrappedTrait<SubTraitT>>
{
typedef jl_array_t* type;
};
template<typename T>
struct julia_type_factory<Array<T>>
{
static inline jl_datatype_t* julia_type()
{
create_if_not_exists<T>();
return (jl_datatype_t*)apply_array_type(jlcxx::julia_type<T>(), 1);
}
};
template<typename T, int Dim>
struct ConvertToJulia<ArrayRef<T,Dim>>
{
template<typename ArrayRefT>
jl_array_t* operator()(ArrayRefT&& arr) const
{
return arr.wrapped();
}
};
template<typename T>
struct ConvertToJulia<Array<T>>
{
jl_value_t* operator()(Array<T>&& arr) const
{
return (jl_value_t*)arr.wrapped();
}
};
template<typename T, int Dim, typename SubTraitT>
struct ConvertToCpp<ArrayRef<T,Dim>, CxxWrappedTrait<SubTraitT>>
{
ArrayRef<T,Dim> operator()(jl_array_t* arr) const
{
return ArrayRef<T,Dim>(arr);
}
};
// Iterator operator implementation
template<typename PointedT, typename CppT>
bool operator!=(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return r.ptr() != l.ptr();
}
template<typename PointedT, typename CppT>
bool operator==(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return r.ptr() == l.ptr();
}
template<typename PointedT, typename CppT>
bool operator<=(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() <= r.ptr();
}
template<typename PointedT, typename CppT>
bool operator>=(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() >= r.ptr();
}
template<typename PointedT, typename CppT>
bool operator>(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() > r.ptr();
}
template<typename PointedT, typename CppT>
bool operator<(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() < r.ptr();
}
template<typename PointedT, typename CppT>
array_iterator_base<PointedT, CppT> operator+(const array_iterator_base<PointedT, CppT>& l, const std::ptrdiff_t n)
{
return array_iterator_base<PointedT, CppT>(l.ptr() + n);
}
template<typename PointedT, typename CppT>
array_iterator_base<PointedT, CppT> operator+(const std::ptrdiff_t n, const array_iterator_base<PointedT, CppT>& r)
{
return array_iterator_base<PointedT, CppT>(r.ptr() + n);
}
template<typename PointedT, typename CppT>
array_iterator_base<PointedT, CppT> operator-(const array_iterator_base<PointedT, CppT>& l, const std::ptrdiff_t n)
{
return array_iterator_base<PointedT, CppT>(l.ptr() - n);
}
template<typename PointedT, typename CppT>
std::ptrdiff_t operator-(const array_iterator_base<PointedT, CppT>& l, const array_iterator_base<PointedT, CppT>& r)
{
return l.ptr() - r.ptr();
}
}
#endif

194
include/jlcxx/attr.hpp Normal file
View File

@@ -0,0 +1,194 @@
#ifndef JLCXX_ATTR_HPP
#define JLCXX_ATTR_HPP
#include <string>
#include <vector>
#include <type_traits>
#include <functional>
#include "jlcxx_config.hpp"
#include "type_conversion.hpp"
// This header provides internal helper functionality for providing additional information like argument names and default arguments for C++ functions (method in module.hpp)
namespace jlcxx
{
namespace detail
{
/// Helper type for function arguments
template <bool IsKwArg>
struct JLCXX_API BasicArg
{
static constexpr bool isKeywordArgument = IsKwArg;
const char *name = nullptr;
jl_value_t* defaultValue = nullptr;
BasicArg(const char *name_) : name(name_) {}
template <typename T>
inline BasicArg &operator=(T value)
{
defaultValue = box<T>(std::forward<T>(value));
return *this;
}
};
}
/// use jlcxx::arg("argumentName") to add function argument names, and jlcxx::arg("name")=value to define an argument with a default value
using arg = detail::BasicArg<false>;
///! use jlcxx::kwarg("argumentName") to define a keyword argument and with jlcxx::kwarg("name")=value you can add a default value for the argument
using kwarg = detail::BasicArg<true>;
/// enum for the force_convert parameter for raw function pointers
enum class calling_policy : bool
{
ccall = false,
std_function = true
};
/// default value for the calling_policy argument for Module::method with raw C++ function pointers
constexpr auto default_calling_policy = calling_policy::ccall;
/// enum for finalize parameter for constructors
enum class finalize_policy : bool
{
no = false,
yes = true
};
/// default value for the finalize_policy argument for Module::constructor
constexpr auto default_finalize_policy = finalize_policy::yes;
namespace detail
{
/// SFINEA for argument processing, inspired/copied from pybind11 code (pybind11/attr.h)
template<typename T, typename SFINEA = void>
struct process_attribute;
/// helper type for parsing argument and docstrings
struct ExtraFunctionData
{
std::vector<arg> positionalArguments;
std::vector<kwarg> keywordArguments;
std::string doc;
calling_policy force_convert = default_calling_policy;
finalize_policy finalize = default_finalize_policy;
};
/// process docstring
template<>
struct process_attribute<const char*>
{
static inline void init(const char* s, ExtraFunctionData& f)
{
f.doc = s;
}
};
template<>
struct process_attribute<char*> : public process_attribute<const char*> {};
/// process positional argument
template<>
struct process_attribute<arg>
{
static inline void init(arg&& a, ExtraFunctionData& f)
{
f.positionalArguments.emplace_back(std::move(a));
}
};
/// process keyword argument
template<>
struct process_attribute<kwarg>
{
static inline void init(kwarg&& a, ExtraFunctionData& f)
{
f.keywordArguments.emplace_back(std::move(a));
}
};
/// process calling_policy argument
template<>
struct process_attribute<calling_policy>
{
static inline void init(calling_policy force_convert, ExtraFunctionData& f)
{
f.force_convert = force_convert;
}
};
/// process finalize_policy argument
template<>
struct process_attribute<finalize_policy>
{
static inline void init(finalize_policy finalize, ExtraFunctionData& f)
{
f.finalize = finalize;
}
};
template<typename T>
void parse_attributes_helper(ExtraFunctionData& f, T argi)
{
using T_ = typename std::decay_t<T>;
process_attribute<T>::init(std::forward<T_>(argi), f);
}
/// initialize ExtraFunctionData from argument list
template<bool AllowCallingPolicy = false, bool AllowFinalizePolicy = false, typename... Extra>
ExtraFunctionData parse_attributes(Extra... extra)
{
// check that the calling_policy is only set if explicitly allowed
constexpr bool contains_calling_policy = (std::is_same_v<calling_policy, Extra> || ... );
static_assert( (!contains_calling_policy) || AllowCallingPolicy, "calling_policy can only be set for raw function pointers!");
// chat that the finalize_policy is only set if explicitly allowed
constexpr bool contains_finalize_policy = (std::is_same_v<finalize_policy, Extra> || ... );
static_assert( (!contains_finalize_policy) || AllowFinalizePolicy, "finalize_policy can only be set for constructors!");
ExtraFunctionData result;
(parse_attributes_helper(result, std::move(extra)), ...);
return result;
}
/// count occurences of specific type in parameter pack
template<typename T, typename... Extra>
constexpr int count_attributes()
{
return (0 + ... + int(std::is_same_v<T,Extra>));
}
static_assert(count_attributes<float, int, float, int, double>() == 1);
static_assert(count_attributes<int, int, float, int, double, int, int>() == 4);
/// check number of arguments matches annotated arguments if annotations for keyword arguments are present
template<typename... Extra>
constexpr bool check_extra_argument_count(int n_arg)
{
// with keyword arguments, the number of annotated arguments must match the number of actual arguments
constexpr auto n_extra_arg = count_attributes<arg, Extra...>();
constexpr auto n_extra_kwarg = count_attributes<kwarg, Extra...>();
return n_extra_kwarg == 0 || n_arg == n_extra_arg + n_extra_kwarg;
}
/// simple helper for checking if a template argument has a call operator (e.g. is a lambda)
template<class T, typename SFINEA = void>
struct has_call_operator : std::false_type {};
template<class T>
struct has_call_operator<T, std::void_t<decltype(&T::operator())>> : std::true_type {};
static_assert(!has_call_operator<const char*>::value);
static_assert(has_call_operator<std::function<void()>>::value);
}
}
#endif

View File

@@ -0,0 +1,124 @@
#ifndef JLCXX_CONST_ARRAY_HPP
#define JLCXX_CONST_ARRAY_HPP
#include "jlcxx.hpp"
#include "tuple.hpp"
namespace jlcxx
{
using index_t = cxxint_t;
namespace detail
{
// Helper to make a C++ tuple of longs based on the number of elements
template<index_t N, typename... TypesT>
struct LongNTuple
{
typedef typename LongNTuple<N-1, index_t, TypesT...>::type type;
};
template<typename... TypesT>
struct LongNTuple<0, TypesT...>
{
typedef std::tuple<TypesT...> type;
};
}
/// Wrap a pointer, providing the Julia array interface for it
/// The parameter N represents the number of dimensions
template<typename T, index_t N>
class ConstArray
{
public:
typedef typename detail::LongNTuple<N>::type size_t;
template<typename... SizesT>
ConstArray(const T* ptr, const SizesT... sizes) :
m_arr(ptr),
m_sizes(sizes...)
{
}
T getindex(const cxxint_t i) const
{
return m_arr[i-1];
}
size_t size() const
{
return m_sizes;
}
const T* ptr() const
{
return m_arr;
}
private:
const T* m_arr;
const size_t m_sizes;
};
template<typename T, typename... SizesT>
ConstArray<T, sizeof...(SizesT)> make_const_array(const T* p, const SizesT... sizes)
{
return ConstArray<T, sizeof...(SizesT)>(p, sizes...);
}
struct ConstArrayTrait {};
template<typename T, index_t N>
struct TraitSelector<ConstArray<T,N>>
{
using type = ConstArrayTrait;
};
template<typename T, index_t N>
struct MappingTrait<ConstArray<T,N>, ConstArrayTrait>
{
using type = ConstArrayTrait;
};
template<typename T, index_t N>
struct ConvertToJulia<ConstArray<T,N>, ConstArrayTrait>
{
jl_value_t* operator()(const ConstArray<T,N>& arr)
{
jl_value_t* result;
jl_value_t* ptr = nullptr;
jl_value_t* size = nullptr;
JL_GC_PUSH2(&ptr, &size);
ptr = box<const T*>(arr.ptr());
size = convert_to_julia(arr.size());
result = jl_new_struct(julia_type<ConstArray<T,N>>(), ptr, size);
JL_GC_POP();
return result;
}
};
template<typename T, index_t N>
struct static_type_mapping<ConstArray<T,N>, ConstArrayTrait>
{
typedef jl_value_t* type;
};
template<typename T, index_t N>
struct julia_type_factory<ConstArray<T,N>, ConstArrayTrait>
{
static jl_datatype_t* julia_type()
{
create_if_not_exists<T>();
jl_value_t* pdt = ::jlcxx::julia_type("ConstArray");
jl_value_t* val = box<index_t>(N);
jl_value_t* result;
JL_GC_PUSH1(&val);
jl_value_t* t[2] = { (jl_value_t*)::jlcxx::julia_type<T>(), val };
result = apply_type(pdt, t, 2);
JL_GC_POP();
return (jl_datatype_t*)result;
}
};
} // namespace jlcxx
#endif

238
include/jlcxx/functions.hpp Normal file
View File

@@ -0,0 +1,238 @@
#ifndef JLCXX_FUNCTIONS_HPP
#define JLCXX_FUNCTIONS_HPP
#include <sstream>
#include <vector>
#include "array.hpp"
#include "type_conversion.hpp"
// This header provides helper functions to call Julia functions from C++
namespace jlcxx
{
/// Wrap a Julia function for easy calling
class JLCXX_API JuliaFunction
{
public:
/// Construct using a function name and module name. Searches the current module by default. Throws if the function was not found.
JuliaFunction(const std::string& name, const std::string& module_name = "");
/// Construct directly from a pointer (throws if pointer is null)
JuliaFunction(jl_function_t* fpointer);
/// Access to the raw pointer
jl_function_t* pointer() const
{
return m_function;
}
/// Call a julia function, converting the arguments to the corresponding Julia types
template<typename... ArgumentsT>
jl_value_t* operator()(ArgumentsT&&... args) const;
private:
struct StoreArgs
{
StoreArgs(jl_value_t** arg_array) : m_arg_array(arg_array)
{
}
template<typename ArgT, typename... ArgsT>
void push(ArgT&& a, ArgsT&&... args)
{
push(std::forward<ArgT>(a));
push(std::forward<ArgsT>(args)...);
}
template<typename ArgT>
void push(ArgT&& a)
{
m_arg_array[m_i++] = box<ArgT>(a);
}
void push(jl_value_t* a)
{
m_arg_array[m_i++] = a;
}
void push() {}
jl_value_t** m_arg_array;
int m_i = 0;
};
jl_function_t* m_function;
};
template<typename... ArgumentsT>
jl_value_t* JuliaFunction::operator()(ArgumentsT&&... args) const
{
(create_if_not_exists<ArgumentsT>(), ...);
const int nb_args = sizeof...(args);
jl_value_t** julia_args;
JL_GC_PUSHARGS(julia_args, nb_args+1); // The last element is the result
// Process arguments
StoreArgs store_args(julia_args);
store_args.push(std::forward<ArgumentsT>(args)...);
for(int i = 0; i != nb_args; ++i)
{
if(julia_args[i] == nullptr)
{
JL_GC_POP();
std::stringstream sstr;
sstr << "Unsupported Julia function argument type at position " << i;
throw std::runtime_error(sstr.str());
}
}
// Do the call
julia_args[nb_args] = jl_call(m_function, julia_args, nb_args);
if (jl_exception_occurred())
{
jl_call2(jl_get_function(jl_base_module, "showerror"), jl_stderr_obj(), jl_exception_occurred());
jl_printf(jl_stderr_stream(), "\n");
JL_GC_POP();
return nullptr;
}
JL_GC_POP();
return julia_args[nb_args];
}
/// Data corresponds to immutable with the same name on the Julia side
struct SafeCFunction
{
void* fptr;
jl_datatype_t* return_type;
jl_array_t* argtypes;
};
// Direct conversion
template<> struct static_type_mapping<SafeCFunction>
{
typedef SafeCFunction type;
};
template<> struct julia_type_factory<SafeCFunction>
{
static jl_datatype_t* julia_type() { return (jl_datatype_t*)jlcxx::julia_type("SafeCFunction"); }
};
template<>
struct ConvertToCpp<SafeCFunction>
{
SafeCFunction operator()(const SafeCFunction& julia_value) const
{
return julia_value;
}
SafeCFunction operator()(jl_value_t* julia_value) const
{
return *reinterpret_cast<SafeCFunction*>(jl_data_ptr(julia_value));
}
};
namespace detail
{
template<typename SignatureT>
struct SplitSignature;
template<typename R, typename... ArgsT>
struct SplitSignature<R(ArgsT...)>
{
typedef R return_type;
typedef R(*fptr_t)(ArgsT...);
std::vector<jl_datatype_t*> operator()()
{
return std::vector<jl_datatype_t*>({julia_type<ArgsT>()...});
}
fptr_t cast_ptr(void* ptr)
{
return reinterpret_cast<fptr_t>(ptr);
}
};
}
/// Type-checking on return type and arguments of a cfunction (void* pointer)
template<typename SignatureT>
typename detail::SplitSignature<SignatureT>::fptr_t make_function_pointer(SafeCFunction data)
{
typedef detail::SplitSignature<SignatureT> SplitterT;
JL_GC_PUSH3(&data.fptr, &data.return_type, &data.argtypes);
// Check return type
jl_datatype_t* expected_rt = julia_type<typename SplitterT::return_type>();
if(expected_rt != data.return_type)
{
JL_GC_POP();
throw std::runtime_error("Incorrect datatype for cfunction return type, expected " + julia_type_name(expected_rt) + " but got " + julia_type_name(data.return_type));
}
// Check arguments
const std::vector<jl_datatype_t*> expected_argstypes = SplitterT()();
ArrayRef<jl_value_t*> argtypes(data.argtypes);
const int nb_args = expected_argstypes.size();
if(nb_args != static_cast<int>(argtypes.size()))
{
std::stringstream err_sstr;
err_sstr << "Incorrect number of arguments for cfunction, expected: " << nb_args << ", obtained: " << argtypes.size();
JL_GC_POP();
throw std::runtime_error(err_sstr.str());
}
for(int i = 0; i != nb_args; ++i)
{
jl_datatype_t* argt = (jl_datatype_t*)argtypes[i];
if(argt != expected_argstypes[i])
{
std::stringstream err_sstr;
err_sstr << "Incorrect argument type for cfunction at position " << i+1 << ", expected: " << julia_type_name(expected_argstypes[i]) << ", obtained: " << julia_type_name(argt);
JL_GC_POP();
throw std::runtime_error(err_sstr.str());
}
}
JL_GC_POP();
return SplitterT().cast_ptr(data.fptr);
}
struct FunctionPtrTrait {};
template<typename R, typename...ArgsT>
struct MappingTrait<R(*)(ArgsT...)>
{
using type = FunctionPtrTrait;
};
/// Implicit conversion to pointer type
template<typename R, typename...ArgsT> struct static_type_mapping<R(*)(ArgsT...)>
{
typedef SafeCFunction type;
};
template<typename R, typename...ArgsT> struct julia_type_factory<R(*)(ArgsT...)>
{
static jl_datatype_t* julia_type()
{
create_if_not_exists<R>();
(create_if_not_exists<ArgsT>(), ...);
return (jl_datatype_t*)jlcxx::julia_type("SafeCFunction");
}
};
template<typename R, typename...ArgsT>
struct ConvertToCpp<R(*)(ArgsT...), FunctionPtrTrait>
{
typedef R(*fptr_t)(ArgsT...);
fptr_t operator()(const SafeCFunction& julia_value) const
{
return make_function_pointer<R(ArgsT...)>(julia_value);
}
};
}
#endif

19
include/jlcxx/jlcxx.hpp Normal file
View File

@@ -0,0 +1,19 @@
#ifndef JLCXX_HPP
#define JLCXX_HPP
#include <cassert>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <sstream>
#include <typeinfo>
#include <vector>
#include "julia_headers.hpp"
#include "array.hpp"
#include "module.hpp"
#include "smart_pointers.hpp"
#include "type_conversion.hpp"
#endif

View File

@@ -0,0 +1,31 @@
#ifndef JLCXX_CONFIG_HPP
#define JLCXX_CONFIG_HPP
#ifdef _WIN32
#ifdef JLCXX_EXPORTS
#define JLCXX_API __declspec(dllexport)
#else
#define JLCXX_API __declspec(dllimport)
#endif
#define JLCXX_ONLY_EXPORTS __declspec(dllexport)
#else
#define JLCXX_API __attribute__ ((visibility("default")))
#define JLCXX_ONLY_EXPORTS JLCXX_API
#endif
#define JLCXX_VERSION_MAJOR 0
#define JLCXX_VERSION_MINOR 12
#define JLCXX_VERSION_PATCH 3
// From https://stackoverflow.com/questions/5459868/concatenate-int-to-string-using-c-preprocessor
#define __JLCXX_STR_HELPER(x) #x
#define __JLCXX_STR(x) __JLCXX_STR_HELPER(x)
#define JLCXX_VERSION_STRING __JLCXX_STR(JLCXX_VERSION_MAJOR) "." __JLCXX_STR(JLCXX_VERSION_MINOR) "." __JLCXX_STR(JLCXX_VERSION_PATCH)
#if defined(__has_include) && !defined(__FreeBSD__)
# if __has_include (<ranges>)
# define JLCXX_HAS_RANGES
# endif
#endif
#endif

View File

@@ -0,0 +1,12 @@
#ifndef JLCXX_JULIA_HEADERS_HPP
#define JLCXX_JULIA_HEADERS_HPP
#ifdef _MSC_VER
#include <uv.h>
#include <windows.h>
#endif
#include <julia.h>
#include <julia_threads.h>
#endif

1417
include/jlcxx/module.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,314 @@
#ifndef JLCXX_SMART_POINTER_HPP
#define JLCXX_SMART_POINTER_HPP
#include "module.hpp"
#include "type_conversion.hpp"
namespace jlcxx
{
struct NoSmartOther {};
struct NoSmartBase {};
template<typename T> struct IsSmartPointerType<std::shared_ptr<T>> : std::true_type { };
template<typename T> struct IsSmartPointerType<std::unique_ptr<T>> : std::true_type { };
template<typename T> struct IsSmartPointerType<std::weak_ptr<T>> : std::true_type { };
/// Override to indicate what smart pointer type is a valid constructor argument, e.g. shared_ptr can be used to construct a weak_ptr
template<typename T> struct ConstructorPointerType { typedef NoSmartOther type; };
template<typename T> struct ConstructorPointerType<std::weak_ptr<T>> { typedef std::shared_ptr<T> type; };
namespace smartptr
{
template<typename T>
struct DereferenceSmartPointer
{
static auto& apply(T& smart_ptr)
{
return *smart_ptr;
}
};
// std::weak_ptr requires a call to lock()
template<typename T>
struct DereferenceSmartPointer<std::weak_ptr<T>>
{
static auto& apply(std::weak_ptr<T>& smart_ptr)
{
return *(smart_ptr.lock());
}
};
template<typename ToType, typename FromType> struct
ConstructFromOther
{
static ToType apply(FromType& from_ptr)
{
return ToType(from_ptr);
}
};
// Conversion to base type
template<typename T>
struct ConvertToBase
{
static NoSmartBase apply(const T&)
{
static_assert(sizeof(T)==0, "No appropriate specialization for ConvertToBase");
return NoSmartBase();
}
};
template<template<typename...> class PtrT, typename T>
struct ConvertToBase<PtrT<T>>
{
using SuperPtrT = PtrT<supertype<T>>;
static PtrT<supertype<T>> apply(const PtrT<T> &smart_ptr)
{
return PtrT<supertype<T>>(smart_ptr);
}
};
// Conversion to const version
template<typename T>
struct ConvertToConst
{
static void wrap(Module&)
{
}
};
namespace detail {
template<template<typename...> class PtrT, typename T>
auto apply_impl(const PtrT<T>& smart_ptr, int) -> decltype(PtrT<const T>(smart_ptr))
{
return PtrT<const T>(smart_ptr);
}
template<template<typename...> class PtrT, typename T>
PtrT<T> apply_impl(const PtrT<T>& smart_ptr, double)
{
throw std::runtime_error(std::string("Const convert not available for ") + typeid(PtrT<T>).name());
return smart_ptr;
}
}
template<template<typename...> class PtrT, typename T>
struct ConvertToConst<PtrT<T>>
{
static auto apply(const PtrT<typename std::remove_const<T>::type>& smart_ptr)
{
return detail::apply_impl(smart_ptr, 0);
}
static void wrap(Module& mod)
{
mod.method("__cxxwrap_make_const_smartptr", &apply);
}
};
template<typename T>
struct ConvertToConst<std::unique_ptr<T>>
{
static void wrap(Module&)
{
}
};
namespace detail
{
template<typename PtrT, typename OtherPtrT>
struct SmartPtrMethods
{
};
template<typename T>
struct split_other_ptr
{
using other_t = NoSmartOther;
using const_other_t = NoSmartOther;
};
template<template<typename...> class PtrT, typename PointeeT, typename... ExtraArgs>
struct split_other_ptr<PtrT<PointeeT, ExtraArgs...>>
{
using nonconst_pointee_t = typename std::remove_const<PointeeT>::type;
using other_t = PtrT<nonconst_pointee_t, ExtraArgs...>;
using const_other_t = PtrT<const nonconst_pointee_t, ExtraArgs...>;
};
template<template<typename...> class PtrT, typename PointeeT, typename OtherPtrT, typename... ExtraArgs>
struct SmartPtrMethods<PtrT<PointeeT, ExtraArgs...>, OtherPtrT>
{
using NonConstPointeeT = typename std::remove_const<PointeeT>::type;
using WrappedT = PtrT<NonConstPointeeT, ExtraArgs...>;
using ConstWrappedT = PtrT<const NonConstPointeeT, ExtraArgs...>;
using ConstOtherPtrT = typename split_other_ptr<OtherPtrT>::const_other_t;
using NonConstOtherPtrT = typename split_other_ptr<OtherPtrT>::other_t;
template<bool B, typename DummyT=void>
struct ConditionalConstructFromOther
{
static void apply(Module& mod)
{
mod.method("__cxxwrap_smartptr_construct_from_other", [] (SingletonType<WrappedT>, NonConstOtherPtrT& ptr) { return ConstructFromOther<WrappedT, NonConstOtherPtrT>::apply(ptr); });
mod.method("__cxxwrap_smartptr_construct_from_other", [] (SingletonType<ConstWrappedT>, ConstOtherPtrT& ptr) { return ConstructFromOther<ConstWrappedT, ConstOtherPtrT>::apply(ptr); });
}
};
template<typename DummyT> struct ConditionalConstructFromOther<false, DummyT> { static void apply(Module&) {} };
template<bool B, typename DummyT=void>
struct ConditionalCastToBase
{
static void apply(Module& mod)
{
mod.method("__cxxwrap_smartptr_cast_to_base", [] (const WrappedT& ptr) { return ConvertToBase<WrappedT>::apply(ptr); });
mod.method("__cxxwrap_smartptr_cast_to_base", [] (const ConstWrappedT& ptr) { return ConvertToBase<ConstWrappedT>::apply(ptr); });
}
};
template<typename DummyT> struct ConditionalCastToBase<false, DummyT> { static void apply(Module&) {} };
static void apply(Module& mod)
{
assert(has_julia_type<WrappedT>());
mod.set_override_module(get_cxxwrap_module());
ConvertToConst<WrappedT>::wrap(mod);
ConditionalConstructFromOther<!std::is_same<OtherPtrT, NoSmartOther>::value>::apply(mod);
ConditionalCastToBase<!std::is_same<NonConstPointeeT,supertype<NonConstPointeeT>>::value && !std::is_same<std::unique_ptr<NonConstPointeeT>, WrappedT>::value>::apply(mod);
mod.unset_override_module();
}
};
}
JLCXX_API void set_smartpointer_type(const type_hash_t& hash, TypeWrapper1* new_wrapper);
JLCXX_API TypeWrapper1* get_smartpointer_type(const type_hash_t& hash);
template<template<typename...> class T>
TypeWrapper1 smart_ptr_wrapper(Module& module)
{
static TypeWrapper1* stored_wrapper = get_smartpointer_type(type_hash<T<int>>());
if(stored_wrapper == nullptr)
{
std::cerr << "Smart pointer type has no wrapper" << std::endl;
abort();
}
return std::move(TypeWrapper1(module, *stored_wrapper));
}
struct WrapSmartPointer
{
template<typename TypeWrapperT>
void operator()(TypeWrapperT&& wrapped)
{
using WrappedT = typename TypeWrapperT::type;
wrapped.module().set_override_module(get_cxxwrap_module());
wrapped.module().method("__cxxwrap_smartptr_dereference", &DereferenceSmartPointer<WrappedT>::apply);
wrapped.module().unset_override_module();
}
};
} // namespace smartptr
template<template<typename...> class T>
TypeWrapper1& add_smart_pointer(Module& mod, const std::string& name)
{
TypeWrapper1* tw = new TypeWrapper1(mod.add_type<Parametric<TypeVar<1>>>(name, julia_type("SmartPointer", get_cxxwrap_module())));
smartptr::set_smartpointer_type(type_hash<T<int>>(), tw);
return *tw;
}
struct SmartPointerTrait {};
template<typename T>
struct MappingTrait<T, typename std::enable_if<IsSmartPointerType<T>::value>::type>
{
using type = CxxWrappedTrait<SmartPointerTrait>;
};
namespace detail
{
template<typename T>
struct apply_smart_ptr_type
{
};
template<template<typename...> class PtrT, typename T, typename... OtherParamsT>
struct apply_smart_ptr_type<PtrT<T, OtherParamsT...>>
{
void operator()(Module& curmod)
{
smartptr::smart_ptr_wrapper<PtrT>(curmod).template apply<PtrT<T, OtherParamsT...>>(smartptr::WrapSmartPointer());
}
};
template<typename T>
struct get_pointee
{
};
template<template<typename...> class PtrT, typename T, typename... OtherParamsT>
struct get_pointee<PtrT<T, OtherParamsT...>>
{
using pointee_t = typename std::remove_const<T>::type;
using pointer_t = PtrT<pointee_t>;
using const_pointer_t = PtrT<const pointee_t>;
};
}
template<typename T>
struct julia_type_factory<T, CxxWrappedTrait<SmartPointerTrait>>
{
static inline jl_datatype_t* julia_type()
{
using PointeeT = typename detail::get_pointee<T>::pointee_t;
using ConstMappedT = typename detail::get_pointee<T>::const_pointer_t;
using NonConstMappedT = typename detail::get_pointee<T>::pointer_t;
create_if_not_exists<PointeeT>();
if constexpr(!std::is_same<supertype<PointeeT>, PointeeT>::value)
{
create_if_not_exists<typename smartptr::ConvertToBase<NonConstMappedT>::SuperPtrT>();
}
assert(!has_julia_type<NonConstMappedT>());
assert(registry().has_current_module());
Module& curmod = registry().current_module();
detail::apply_smart_ptr_type<NonConstMappedT>()(curmod);
detail::apply_smart_ptr_type<ConstMappedT>()(curmod);
smartptr::detail::SmartPtrMethods<NonConstMappedT, typename ConstructorPointerType<NonConstMappedT>::type>::apply(curmod);
assert(has_julia_type<T>());
return JuliaTypeCache<T>::julia_type();
}
};
namespace smartptr
{
template<template<typename...> class PtrT>
struct WrapSmartPointerCombo
{
template<typename PointeeT>
void operator()()
{
create_julia_type<PtrT<PointeeT>>();
}
};
template<template<typename...> class PtrT, typename TypeListT>
inline void apply_smart_combination()
{
jlcxx::for_each_type<TypeListT>(WrapSmartPointerCombo<PtrT>());
}
}
}
#endif

268
include/jlcxx/stl.hpp Normal file
View File

@@ -0,0 +1,268 @@
#ifndef JLCXX_STL_HPP
#define JLCXX_STL_HPP
#include <valarray>
#include <vector>
#include <deque>
#include <queue>
#include "module.hpp"
#include "smart_pointers.hpp"
#include "type_conversion.hpp"
namespace jlcxx
{
namespace detail
{
struct UnusedT {};
/// Replace T1 by UnusedT if T1 == T2, return T1 otherwise
template<typename T1, typename T2>
struct SkipIfSameAs
{
using type = T1;
};
template<typename T>
struct SkipIfSameAs<T,T>
{
using type = UnusedT;
};
template<typename T1, typename T2> using skip_if_same = typename SkipIfSameAs<T1,T2>::type;
}
namespace stl
{
class JLCXX_API StlWrappers
{
private:
StlWrappers(Module& mod);
static std::unique_ptr<StlWrappers> m_instance;
Module& m_stl_mod;
public:
TypeWrapper1 vector;
TypeWrapper1 valarray;
TypeWrapper1 deque;
TypeWrapper1 queue;
static void instantiate(Module& mod);
static StlWrappers& instance();
inline jl_module_t* module() const
{
return m_stl_mod.julia_module();
}
};
JLCXX_API StlWrappers& wrappers();
using stltypes = remove_duplicates<combine_parameterlists<combine_parameterlists<ParameterList
<
bool,
double,
float,
char,
wchar_t,
void*,
std::string,
std::wstring,
jl_value_t*
>, fundamental_int_types>, fixed_int_types>>;
template<typename TypeWrapperT>
void wrap_range_based_algorithms(TypeWrapperT& wrapped)
{
#ifdef JLCXX_HAS_RANGES
using WrappedT = typename TypeWrapperT::type;
using T = typename WrappedT::value_type;
wrapped.module().set_override_module(StlWrappers::instance().module());
wrapped.method("StdFill", [] (WrappedT& v, const T& val) { std::ranges::fill(v, val); });
wrapped.module().unset_override_module();
#endif
}
template<typename T>
struct WrapVectorImpl
{
template<typename TypeWrapperT>
static void wrap(TypeWrapperT&& wrapped)
{
using WrappedT = std::vector<T>;
wrap_range_based_algorithms(wrapped);
wrapped.module().set_override_module(StlWrappers::instance().module());
wrapped.method("push_back", static_cast<void (WrappedT::*)(const T&)>(&WrappedT::push_back));
wrapped.method("cxxgetindex", [] (const WrappedT& v, cxxint_t i) -> typename WrappedT::const_reference { return v[i-1]; });
wrapped.method("cxxgetindex", [] (WrappedT& v, cxxint_t i) -> typename WrappedT::reference { return v[i-1]; });
wrapped.method("cxxsetindex!", [] (WrappedT& v, const T& val, cxxint_t i) { v[i-1] = val; });
wrapped.module().unset_override_module();
}
};
template<>
struct WrapVectorImpl<bool>
{
template<typename TypeWrapperT>
static void wrap(TypeWrapperT&& wrapped)
{
using WrappedT = std::vector<bool>;
wrapped.module().set_override_module(StlWrappers::instance().module());
wrapped.method("push_back", [] (WrappedT& v, const bool val) { v.push_back(val); });
wrapped.method("cxxgetindex", [] (const WrappedT& v, cxxint_t i) { return bool(v[i-1]); });
wrapped.method("cxxsetindex!", [] (WrappedT& v, const bool val, cxxint_t i) { v[i-1] = val; });
wrapped.module().unset_override_module();
}
};
struct WrapVector
{
template<typename TypeWrapperT>
void operator()(TypeWrapperT&& wrapped)
{
using WrappedT = typename TypeWrapperT::type;
using T = typename WrappedT::value_type;
wrapped.module().set_override_module(StlWrappers::instance().module());
wrapped.method("cppsize", &WrappedT::size);
wrapped.method("resize", [] (WrappedT& v, const cxxint_t s) { v.resize(s); });
wrapped.method("append", [] (WrappedT& v, jlcxx::ArrayRef<T> arr)
{
const std::size_t addedlen = arr.size();
v.reserve(v.size() + addedlen);
for(size_t i = 0; i != addedlen; ++i)
{
v.push_back(arr[i]);
}
});
wrapped.module().unset_override_module();
WrapVectorImpl<T>::wrap(wrapped);
}
};
struct WrapValArray
{
template<typename TypeWrapperT>
void operator()(TypeWrapperT&& wrapped)
{
using WrappedT = typename TypeWrapperT::type;
using T = typename WrappedT::value_type;
wrap_range_based_algorithms(wrapped);
wrapped.template constructor<std::size_t>();
wrapped.template constructor<const T&, std::size_t>();
wrapped.template constructor<const T*, std::size_t>();
wrapped.module().set_override_module(StlWrappers::instance().module());
wrapped.method("cppsize", &WrappedT::size);
wrapped.method("resize", [] (WrappedT& v, const cxxint_t s) { v.resize(s); });
wrapped.method("cxxgetindex", [] (const WrappedT& v, cxxint_t i) -> const T& { return v[i-1]; });
wrapped.method("cxxgetindex", [] (WrappedT& v, cxxint_t i) -> T& { return v[i-1]; });
wrapped.method("cxxsetindex!", [] (WrappedT& v, const T& val, cxxint_t i) { v[i-1] = val; });
wrapped.module().unset_override_module();
}
};
struct WrapDeque
{
template<typename TypeWrapperT>
void operator()(TypeWrapperT&& wrapped)
{
using WrappedT = typename TypeWrapperT::type;
using T = typename WrappedT::value_type;
wrap_range_based_algorithms(wrapped);
wrapped.template constructor<std::size_t>();
wrapped.module().set_override_module(StlWrappers::instance().module());
wrapped.method("cppsize", &WrappedT::size);
wrapped.method("resize", [](WrappedT &v, const cxxint_t s) { v.resize(s); });
wrapped.method("cxxgetindex", [](const WrappedT& v, cxxint_t i) -> const T& { return v[i - 1]; });
wrapped.method("cxxsetindex!", [](WrappedT& v, const T& val, cxxint_t i) { v[i - 1] = val; });
wrapped.method("push_back!", [] (WrappedT& v, const T& val) { v.push_back(val); });
wrapped.method("push_front!", [] (WrappedT& v, const T& val) { v.push_front(val); });
wrapped.method("pop_back!", [] (WrappedT& v) { v.pop_back(); });
wrapped.method("pop_front!", [] (WrappedT& v) { v.pop_front(); });
wrapped.module().unset_override_module();
}
};
template<typename T>
struct WrapQueueImpl
{
template<typename TypeWrapperT>
static void wrap(TypeWrapperT&& wrapped)
{
using WrappedT = std::queue<T>;
wrapped.module().set_override_module(StlWrappers::instance().module());
wrapped.method("cppsize", &WrappedT::size);
wrapped.method("push_back!", [] (WrappedT& v, const T& val) { v.push(val); });
wrapped.method("front", [] (WrappedT& v) { return v.front(); });
wrapped.method("pop_front!", [] (WrappedT& v) { v.pop(); });
wrapped.module().unset_override_module();
}
};
template<>
struct WrapQueueImpl<bool>
{
template<typename TypeWrapperT>
static void wrap(TypeWrapperT&& wrapped)
{
using WrappedT = std::queue<bool>;
wrapped.module().set_override_module(StlWrappers::instance().module());
wrapped.method("cppsize", &WrappedT::size);
wrapped.method("push_back!", [] (WrappedT& v, const bool val) { v.push(val); });
wrapped.method("front", [] (WrappedT& v) -> bool { return v.front(); });
wrapped.method("pop_front!", [] (WrappedT& v) { v.pop(); });
wrapped.module().unset_override_module();
}
};
struct WrapQueue
{
template<typename TypeWrapperT>
void operator()(TypeWrapperT&& wrapped)
{
using WrappedT = typename TypeWrapperT::type;
using T = typename WrappedT::value_type;
WrapQueueImpl<T>::wrap(wrapped);
}
};
template<typename T>
inline void apply_stl(jlcxx::Module& mod)
{
TypeWrapper1(mod, StlWrappers::instance().vector).apply<std::vector<T>>(WrapVector());
TypeWrapper1(mod, StlWrappers::instance().valarray).apply<std::valarray<T>>(WrapValArray());
TypeWrapper1(mod, StlWrappers::instance().deque).apply<std::deque<T>>(WrapDeque());
TypeWrapper1(mod, StlWrappers::instance().queue).apply<std::queue<T>>(WrapQueue());
}
}
template<typename T>
struct julia_type_factory<std::vector<T>>
{
using MappedT = std::vector<T>;
static inline jl_datatype_t* julia_type()
{
create_if_not_exists<T>();
assert(!has_julia_type<MappedT>());
assert(registry().has_current_module());
jl_datatype_t* jltype = ::jlcxx::julia_type<T>();
Module& curmod = registry().current_module();
stl::apply_stl<T>(curmod);
assert(has_julia_type<MappedT>());
return JuliaTypeCache<MappedT>::julia_type();
}
};
}
#endif

146
include/jlcxx/tuple.hpp Normal file
View File

@@ -0,0 +1,146 @@
#ifndef JLCXX_TUPLE_HPP
#define JLCXX_TUPLE_HPP
#include <tuple>
#include "type_conversion.hpp"
namespace jlcxx
{
namespace detail
{
template<std::size_t I, std::size_t N>
struct AppendTupleValues
{
template<typename TupleT>
static void apply(jl_value_t** boxed, const TupleT& tup)
{
boxed[I] = box<std::tuple_element_t<I,TupleT>>(std::get<I>(tup));
AppendTupleValues<I+1, std::tuple_size<TupleT>::value>::apply(boxed, tup);
}
};
template<std::size_t N>
struct AppendTupleValues<N,N>
{
template<typename TupleT>
static void apply(jl_value_t**, const TupleT&)
{
}
};
template<typename TupleT>
jl_value_t* new_jl_tuple(const TupleT& tp)
{
jl_value_t* result = nullptr;
jl_datatype_t* concrete_dt = nullptr;
JL_GC_PUSH2(&result, &concrete_dt);
{
constexpr std::size_t tup_sz = std::tuple_size<TupleT>::value;
jl_value_t** args;
JL_GC_PUSHARGS(args, tup_sz);
detail::AppendTupleValues<0, tup_sz>::apply(args, tp);
{
jl_value_t** concrete_types;
JL_GC_PUSHARGS(concrete_types, tup_sz);
for(std::size_t i = 0; i != tup_sz; ++i)
{
concrete_types[i] = jl_typeof(args[i]);
}
concrete_dt = (jl_datatype_t*) jl_apply_tuple_type_v(concrete_types, tup_sz);
JL_GC_POP();
}
result = jl_new_structv(concrete_dt, args, tup_sz);
JL_GC_POP();
}
JL_GC_POP();
return result;
}
}
struct TupleTrait {};
template<typename... TypesT>
struct TraitSelector<std::tuple<TypesT...>>
{
using type = TupleTrait;
};
template<typename... TypesT>
struct MappingTrait<std::tuple<TypesT...>, TupleTrait>
{
using type = TupleTrait;
};
template<typename... TypesT> struct static_type_mapping<std::tuple<TypesT...>, TupleTrait>
{
using type = jl_value_t*;
};
template<typename... TypesT> struct julia_type_factory<std::tuple<TypesT...>, TupleTrait>
{
static jl_datatype_t* julia_type()
{
(create_if_not_exists<TypesT>(), ...);
jl_svec_t *params = nullptr;
jl_datatype_t* result = nullptr;
JL_GC_PUSH1(&params);
params = jl_svec(sizeof...(TypesT), jlcxx::julia_type<TypesT>()...);
#if (JULIA_VERSION_MAJOR * 100 + JULIA_VERSION_MINOR) >= 111
result = (jl_datatype_t*) jl_apply_tuple_type(params,1);
#else
result = (jl_datatype_t*) jl_apply_tuple_type(params);
#endif
JL_GC_POP();
return result;
}
};
template<typename... TypesT>
struct ConvertToJulia<std::tuple<TypesT...>, TupleTrait>
{
jl_value_t* operator()(const std::tuple<TypesT...>& tp)
{
return detail::new_jl_tuple(tp);
}
};
// Wrap NTuple type
template<typename N, typename T>
struct NTuple
{
};
template<typename N, typename T>
struct TraitSelector<NTuple<N,T>>
{
using type = TupleTrait;
};
template<typename N, typename T>
struct MappingTrait<NTuple<N,T>, TupleTrait>
{
using type = TupleTrait;
};
template<typename N, typename T>
struct static_type_mapping<NTuple<N,T>, TupleTrait>
{
typedef jl_datatype_t* type;
};
template<typename N, typename T>
struct julia_type_factory<NTuple<N,T>>
{
static jl_datatype_t* julia_type()
{
create_if_not_exists<T>();
jl_value_t* t[2] = { ::jlcxx::julia_type<T>(), ::jlcxx::julia_type<N>() };
jl_value_t* type = apply_type((jl_value_t*)jl_vararg_type, t, 2);
return (jl_datatype_t*) jl_apply_tuple_type_v(&type, 1);
}
};
} // namespace jlcxx
#endif

File diff suppressed because it is too large Load Diff

13
main.cpp Normal file
View File

@@ -0,0 +1,13 @@
#include <julia.h>
JULIA_DEFINE_FAST_TLS
int main(int argc, char *argv[]) {
jl_init();
jl_eval_string(R"(include("main.jl"); Re3DMod();)");
//jl_eval_string("println(420)");
jl_atexit_hook(0);
return 0;
}

22
main.jl Normal file
View File

@@ -0,0 +1,22 @@
module CppHello
using CxxWrap
@wrapmodule(() -> joinpath("./build", "libhello"))
function __init__()
@initcxx
end
end
function Re3DMod()
println(CppHello.greet())
println("""
This is a test of how a Re3D plugin/mod can work with components of the Re3D game engine. Plugins/mods can be written in Julia. Julia is a performant just in time compiled (JIT) language. Julia is easily embeddable into C++/C programs. Julia also has great interopt with C++/C however the former requires a library, so using C++ components are less painful. Re3D takes care of exposing the API for Julia. All Julia needs to do is define a module which automatically loads the C++ API for the game engine. While this implementation is very primitive and not included in the engine as of now there is potential in it.
""")
println("""
Plugins/mods written in Julia may also be changed easily. No recompilation of the engine required! Although it may be possible it's not a good idea to make changes to mods/plugins as the engine is running them. I would also suggest against reloading them in order to apply the new changes. Unexpected bugs may occur during runtime.
""")
end