Skip to content

Commit b00e22b

Browse files
authored
Remove type map (#161)
* Remove type map * Use hashmap for type mapping only on Windows See comments in jlcxx.cpp for details
1 parent 93768c1 commit b00e22b

File tree

6 files changed

+102
-108
lines changed

6 files changed

+102
-108
lines changed

include/jlcxx/jlcxx_config.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define JLCXX_CONFIG_HPP
33

44
#ifdef _WIN32
5+
#define JLCXX_USE_TYPE_MAP
56
#ifdef JLCXX_EXPORTS
67
#define JLCXX_API __declspec(dllexport)
78
#else
@@ -14,8 +15,8 @@
1415
#endif
1516

1617
#define JLCXX_VERSION_MAJOR 0
17-
#define JLCXX_VERSION_MINOR 12
18-
#define JLCXX_VERSION_PATCH 5
18+
#define JLCXX_VERSION_MINOR 13
19+
#define JLCXX_VERSION_PATCH 0
1920

2021
// From https://stackoverflow.com/questions/5459868/concatenate-int-to-string-using-c-preprocessor
2122
#define __JLCXX_STR_HELPER(x) #x

include/jlcxx/module.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,10 @@ class TypeWrapper
12351235

12361236
using TypeWrapper1 = TypeWrapper<Parametric<TypeVar<1>>>;
12371237

1238+
#ifdef JLCXX_USE_TYPE_MAP
1239+
JLCXX_API std::shared_ptr<TypeWrapper1>& jlcxx_smartpointer_type(std::type_index idx);
1240+
#endif
1241+
12381242
template<typename ApplyT, typename... TypeLists> using combine_types = typename CombineTypes<ApplyT, TypeLists...>::type;
12391243

12401244
template<typename T>

include/jlcxx/smart_pointers.hpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,34 @@ struct SmartPtrMethods<PtrT<PointeeT, ExtraArgs...>, OtherPtrT>
186186

187187
}
188188

189-
JLCXX_API void set_smartpointer_type(const type_hash_t& hash, TypeWrapper1* new_wrapper);
190-
JLCXX_API TypeWrapper1* get_smartpointer_type(const type_hash_t& hash);
189+
template<typename T>
190+
inline std::shared_ptr<TypeWrapper1>& stored_smartpointer_type()
191+
{
192+
#ifdef JLCXX_USE_TYPE_MAP
193+
return jlcxx_smartpointer_type(typeid(T));
194+
#else
195+
static std::shared_ptr<TypeWrapper1> m_ptr;
196+
return m_ptr;
197+
#endif
198+
}
199+
200+
template<typename T>
201+
void set_smartpointer_type(TypeWrapper1* new_wrapper)
202+
{
203+
assert(stored_smartpointer_type<T>().get() == nullptr);
204+
stored_smartpointer_type<T>().reset(new_wrapper);
205+
}
206+
207+
template<typename T>
208+
TypeWrapper1* get_smartpointer_type()
209+
{
210+
return stored_smartpointer_type<T>().get();
211+
}
191212

192213
template<template<typename...> class T>
193214
TypeWrapper1 smart_ptr_wrapper(Module& module)
194215
{
195-
static TypeWrapper1* stored_wrapper = get_smartpointer_type(type_hash<T<int>>());
216+
static TypeWrapper1* stored_wrapper = get_smartpointer_type<T<int>>();
196217
if(stored_wrapper == nullptr)
197218
{
198219
std::cerr << "Smart pointer type has no wrapper" << std::endl;
@@ -220,7 +241,7 @@ template<template<typename...> class T>
220241
TypeWrapper1& add_smart_pointer(Module& mod, const std::string& name)
221242
{
222243
TypeWrapper1* tw = new TypeWrapper1(mod.add_type<Parametric<TypeVar<1>>>(name, julia_type("SmartPointer", get_cxxwrap_module())));
223-
smartptr::set_smartpointer_type(type_hash<T<int>>(), tw);
244+
smartptr::set_smartpointer_type<T<int>>(tw);
224245
return *tw;
225246
}
226247

@@ -284,7 +305,7 @@ struct julia_type_factory<T, CxxWrappedTrait<SmartPointerTrait>>
284305
detail::apply_smart_ptr_type<ConstMappedT>()(curmod);
285306
smartptr::detail::SmartPtrMethods<NonConstMappedT, typename ConstructorPointerType<NonConstMappedT>::type>::apply(curmod);
286307
assert(has_julia_type<T>());
287-
return JuliaTypeCache<T>::julia_type();
308+
return stored_type<T>().get_dt();
288309
}
289310
};
290311

include/jlcxx/stl.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ struct julia_type_factory<std::vector<T>>
351351
Module& curmod = registry().current_module();
352352
stl::apply_stl<T>(curmod);
353353
assert(has_julia_type<MappedT>());
354-
return JuliaTypeCache<MappedT>::julia_type();
354+
return stored_type<MappedT>().get_dt();
355355
}
356356
};
357357

include/jlcxx/type_conversion.hpp

Lines changed: 37 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ struct static_type_mapping<BoxedValue<T>>
322322
template<typename T> using static_julia_type = typename static_type_mapping<T>::type;
323323

324324
// Store a data type pointer, ensuring GC safety
325-
struct CachedDatatype
325+
struct JLCXX_API CachedDatatype
326326
{
327327
explicit CachedDatatype() : m_dt(nullptr) {}
328328
explicit CachedDatatype(jl_datatype_t* dt, bool protect = true)
@@ -348,112 +348,64 @@ struct CachedDatatype
348348
jl_datatype_t* m_dt = nullptr;
349349
};
350350

351-
// Work around the fact that references aren't part of the typeid result
352-
using type_hash_t = std::pair<std::type_index, std::size_t>;
353351

354-
} // namespace jlcxx
352+
#ifdef JLCXX_USE_TYPE_MAP
355353

356-
namespace std {
357-
358-
// Hash implementation from https://en.cppreference.com/w/cpp/utility/hash
359-
template<>
360-
struct hash<jlcxx::type_hash_t>
361-
{
362-
std::size_t operator()(const jlcxx::type_hash_t& h) const noexcept
363-
{
364-
std::size_t h1 = std::hash<std::type_index>{}(h.first);
365-
std::size_t h2 = std::hash<std::size_t>{}(h.second);
366-
return h1 ^ (h2 << 1);
367-
}
368-
};
369-
370-
}
371-
372-
namespace jlcxx
373-
{
374-
375-
namespace detail
376-
{
354+
JLCXX_API CachedDatatype& jlcxx_type(std::type_index idx);
355+
JLCXX_API CachedDatatype& jlcxx_reftype(std::type_index idx);
356+
JLCXX_API CachedDatatype& jlcxx_constreftype(std::type_index idx);
377357

378358
template<typename T>
379-
struct TypeHash
359+
struct HashedCache
380360
{
381-
static inline type_hash_t value()
361+
static inline CachedDatatype& value()
382362
{
383-
return std::make_pair(std::type_index(typeid(T)), std::size_t(0));
363+
return jlcxx_type(typeid(T));
384364
}
385365
};
386366

387367
template<typename T>
388-
struct TypeHash<T&>
368+
struct HashedCache<T&>
389369
{
390-
static inline type_hash_t value()
370+
static inline CachedDatatype& value()
391371
{
392-
return std::make_pair(std::type_index(typeid(T)), std::size_t(1));
372+
return jlcxx_reftype(typeid(T));
393373
}
394374
};
395375

396376
template<typename T>
397-
struct TypeHash<const T&>
377+
struct HashedCache<const T&>
398378
{
399-
static inline type_hash_t value()
379+
static inline CachedDatatype& value()
400380
{
401-
return std::make_pair(std::type_index(typeid(T)), std::size_t(2));
381+
return jlcxx_constreftype(typeid(T));
402382
}
403383
};
404384

405-
}
385+
#endif
406386

407-
template<typename T>
408-
inline type_hash_t type_hash()
387+
template<typename CppT>
388+
CachedDatatype& stored_type()
409389
{
410-
return detail::TypeHash<T>::value();
390+
#ifdef JLCXX_USE_TYPE_MAP
391+
return HashedCache<CppT>::value();
392+
#else
393+
static CachedDatatype m_dt;
394+
return m_dt;
395+
#endif
411396
}
412397

413-
JLCXX_API std::unordered_map<type_hash_t, CachedDatatype>& jlcxx_type_map();
414-
415-
/// Store the Julia datatype linked to SourceT
416-
template<typename SourceT>
417-
class JuliaTypeCache
418-
{
419-
public:
420-
421-
static inline jl_datatype_t* julia_type()
422-
{
423-
const auto result = jlcxx_type_map().find(type_hash<SourceT>());
424-
if(result == jlcxx_type_map().end())
425-
{
426-
throw std::runtime_error("Type " + std::string(typeid(SourceT).name()) + " has no Julia wrapper");
427-
}
428-
return result->second.get_dt();
429-
}
430-
431-
static inline void set_julia_type(jl_datatype_t* dt, bool protect = true)
432-
{
433-
type_hash_t new_hash = type_hash<SourceT>();
434-
const auto [inserted_it, insert_success] = jlcxx_type_map().insert(std::make_pair(new_hash, CachedDatatype(dt, protect)));
435-
if(!insert_success)
436-
{
437-
type_hash_t old_hash = inserted_it->first;
438-
std::cout << "Warning: Type " << new_hash.first.name() << " already had a mapped type set as "
439-
<< julia_type_name(inserted_it->second.get_dt()) << " and const-ref indicator " << old_hash.second << " and C++ type name " << old_hash.first.name()
440-
<< ". Hash comparison: old(" << old_hash.first.hash_code() << "," << old_hash.second << ") == new(" << old_hash.first.hash_code() << "," << old_hash.second << ") == "
441-
<< std::boolalpha << (old_hash == new_hash) << std::endl;
442-
return;
443-
}
444-
}
445-
446-
static inline bool has_julia_type()
447-
{
448-
const std::size_t nb_hits = jlcxx_type_map().count(type_hash<SourceT>());
449-
return nb_hits != 0;
450-
}
451-
};
452-
453398
template<typename T>
454399
void set_julia_type(jl_datatype_t* dt, bool protect = true)
455400
{
456-
JuliaTypeCache<typename std::remove_const<T>::type>::set_julia_type(dt, protect);
401+
using nonconst_t = typename std::remove_const<T>::type;
402+
CachedDatatype& cache = stored_type<nonconst_t>();
403+
if(cache.get_dt() != nullptr)
404+
{
405+
std::cout << "Warning: Type " << typeid(T).name() << " already had a mapped type set as " << julia_type_name(cache.get_dt()) << std::endl;
406+
return;
407+
}
408+
cache.set_dt(dt, protect);
457409
}
458410

459411
/// Store the Julia datatype linked to SourceT
@@ -473,7 +425,11 @@ template<typename T>
473425
inline jl_datatype_t* julia_type()
474426
{
475427
using nonconst_t = typename std::remove_const<T>::type;
476-
static jl_datatype_t* dt = JuliaTypeCache<nonconst_t>::julia_type();
428+
jl_datatype_t* dt = stored_type<nonconst_t>().get_dt();
429+
if(dt == nullptr)
430+
{
431+
throw std::runtime_error("Type " + std::string(typeid(nonconst_t).name()) + " has no Julia wrapper");
432+
}
477433
return dt;
478434
}
479435

@@ -482,7 +438,7 @@ template <typename T>
482438
bool has_julia_type()
483439
{
484440
using nonconst_t = typename std::remove_const<T>::type;
485-
return JuliaTypeCache<nonconst_t>::has_julia_type();
441+
return stored_type<nonconst_t>().get_dt() != nullptr;
486442
}
487443

488444
/// Create the julia type associated with the given C++ type

src/jlcxx.cpp

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -341,38 +341,50 @@ namespace detail
341341
};
342342
}
343343

344-
JLCXX_API std::unordered_map<type_hash_t, CachedDatatype>& jlcxx_type_map()
345-
{
346-
static std::unordered_map<type_hash_t, CachedDatatype> m_map;
347-
return m_map;
348-
}
344+
#ifdef JLCXX_USE_TYPE_MAP
349345

350-
namespace smartptr
351-
{
346+
// On windows, we can't store a mapping from the C++ type to the Julia type
347+
// in a static variable declared in a template function, since each DLL
348+
// will have its onw copy of that variable, making it impossible to share
349+
// type definitions between the CxxWrap base library and other libraries.
350+
// The workaround is to store the types in a map, but this is more fragile
351+
// because the guarantees on std::type_index are not very strong either
352+
// in the context of sharing information between shared libraries. This is
353+
// why we fall back to this approach only on Windows. Using type names is
354+
// also not a solution because types in anonymous namespaces will clash.
355+
// Refs:
356+
// https://stackoverflow.com/questions/398069/static-member-variable-in-template-with-multiple-dlls
357+
// https://developercommunity.visualstudio.com/t/template-static-members-and-multiple-definitions-a/1202888
358+
// https://github.com/pybind/pybind11/pull/4319
359+
// https://bugs.llvm.org/show_bug.cgi?id=33542
360+
// https://github.com/pybind/pybind11/issues/3289
352361

353-
std::map<type_hash_t, std::shared_ptr<TypeWrapper1>>& jlcxx_smartpointer_types()
362+
JLCXX_API CachedDatatype& jlcxx_type(std::type_index idx)
354363
{
355-
static std::map<type_hash_t, std::shared_ptr<TypeWrapper1>> m_map;
356-
return m_map;
364+
static std::unordered_map<std::type_index, CachedDatatype> m_map;
365+
return m_map.insert(std::make_pair(idx,CachedDatatype())).first->second;
357366
}
358367

359-
JLCXX_API void set_smartpointer_type(const type_hash_t& hash, TypeWrapper1* new_wrapper)
368+
JLCXX_API CachedDatatype& jlcxx_reftype(std::type_index idx)
360369
{
361-
jlcxx_smartpointer_types()[hash] = std::shared_ptr<TypeWrapper1>(new_wrapper);
370+
static std::unordered_map<std::type_index, CachedDatatype> m_map;
371+
return m_map.insert(std::make_pair(idx,CachedDatatype())).first->second;
362372
}
363373

364-
JLCXX_API TypeWrapper1* get_smartpointer_type(const type_hash_t& hash)
374+
JLCXX_API CachedDatatype& jlcxx_constreftype(std::type_index idx)
365375
{
366-
auto result = jlcxx_smartpointer_types().find(hash);
367-
if(result == jlcxx_smartpointer_types().end())
368-
{
369-
return nullptr;
370-
}
371-
return result->second.get();
376+
static std::unordered_map<std::type_index, CachedDatatype> m_map;
377+
return m_map.insert(std::make_pair(idx,CachedDatatype())).first->second;
372378
}
373379

380+
JLCXX_API std::shared_ptr<TypeWrapper1>& jlcxx_smartpointer_type(std::type_index idx)
381+
{
382+
static std::unordered_map<std::type_index, std::shared_ptr<TypeWrapper1>> m_map;
383+
return m_map.insert(std::make_pair(idx, nullptr)).first->second;
374384
}
375385

386+
#endif
387+
376388
JLCXX_API void register_core_types()
377389
{
378390
if(jl_base_module == nullptr)

0 commit comments

Comments
 (0)