first commit
This commit is contained in:
23
CMakeLists.txt
Normal file
23
CMakeLists.txt
Normal 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
182
FindJulia.cmake
Normal 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
15
README.md
Normal 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
4
build.sh
Normal 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
13
hello.cpp
Normal 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
463
include/jlcxx/array.hpp
Normal 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
194
include/jlcxx/attr.hpp
Normal 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
|
124
include/jlcxx/const_array.hpp
Normal file
124
include/jlcxx/const_array.hpp
Normal 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
238
include/jlcxx/functions.hpp
Normal 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
19
include/jlcxx/jlcxx.hpp
Normal 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
|
31
include/jlcxx/jlcxx_config.hpp
Normal file
31
include/jlcxx/jlcxx_config.hpp
Normal 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
|
12
include/jlcxx/julia_headers.hpp
Normal file
12
include/jlcxx/julia_headers.hpp
Normal 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
1417
include/jlcxx/module.hpp
Normal file
File diff suppressed because it is too large
Load Diff
314
include/jlcxx/smart_pointers.hpp
Normal file
314
include/jlcxx/smart_pointers.hpp
Normal 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
268
include/jlcxx/stl.hpp
Normal 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
146
include/jlcxx/tuple.hpp
Normal 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(¶ms);
|
||||
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
|
1199
include/jlcxx/type_conversion.hpp
Normal file
1199
include/jlcxx/type_conversion.hpp
Normal file
File diff suppressed because it is too large
Load Diff
13
main.cpp
Normal file
13
main.cpp
Normal 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
22
main.jl
Normal 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
|
||||
|
||||
|
Reference in New Issue
Block a user