Ich versuche, die Basis von std::variant
umschreiben, weil ich einen C++ 14 Compiler (ohne Std :: Variante) verwenden muss und ich brauche eine typsichere enum.Destruction Fehler beim Implementieren Variante
Mein Code funktioniert nur mit Grundtypen (dh int
, float
, char *
, etc.) Aber wenn std::string
zum Beispiel mit, ich habe Probleme mit der Zerstörung.
Hier ist mein Code:
namespace utils
{
namespace // private implementation
{
template<std::size_t Index, typename T, typename... Rest>
struct can_construct_type : std::false_type {}; // End of recursion, T is not in Rest...
template<std::size_t Index, typename T, typename First>
struct can_construct_type<Index, T, First> : std::is_constructible<First, T>
{ // There is only one type remaining to check
static constexpr std::size_t index = Index;
};
template<std::size_t Index, typename T, typename First, typename... Rest>
struct can_construct_type<Index, T, First, Rest...> : std::integral_constant<bool, std::is_constructible<First, T>::value || can_construct_type<Index + 1, T, Rest...>::value>
{ // Main recursive loop. Check for First, then recursively for Rest...
static constexpr std::size_t index = (std::is_constructible<First, T>::value ? Index : can_construct_type<Index + 1, T, Rest...>::index);
};
}
template<typename T, typename... Types>
using can_construct_type = can_construct_type<0, T, Types...>;
}
template<typename... Types>
class variant
{
template<typename _Vp> friend void * __get_storage(_Vp &);
using storage_t = std::aligned_union_t<0u, Types...>;
storage_t m_storage;
int m_type_index;
/* creation of content */
template<typename T> inline void set_value(T const & value)
{
new ((T *)std::addressof(m_storage)) T(value);
m_type_index = utils::can_construct_type<T, Types...>::index;
}
/* destruction of content */
inline void destroy_data(void) { invoke_destructor(m_type_index, std::addressof(m_storage)); }
static void invoke_destructor(int type, storage_t * storage_address)
{
static const std::array<void(*)(storage_t *), sizeof...(Types)> destructors
{
std::addressof(invoke_destructor_impl<Types>)...
};
destructors[type](storage_address);
}
template<class T> static void invoke_destructor_impl(storage_t * storage_address)
{
T * pt = reinterpret_cast<T *>(storage_address);
delete pt;
//pt->~T(); // I tried delete or ->~T() but both crash
}
public:
variant() = delete;
template<typename T> variant(T value)
{
set_value(value);
}
template<typename T> variant & operator=(T & value)
{
destroy_data();
set_value(value);
return *this;
}
~variant()
{
destroy_data();
}
std::size_t index(void) const
{
return m_type_index;
}
};
/* getter */
template<typename Variant> static void * __get_storage(Variant & var)
{
return reinterpret_cast<void *>(std::addressof(var.m_storage));
}
template<typename T, typename... Types> T * get_if(variant<Types...> & var)
{
return (var.index() == utils::contains_type<T, Types...>::index
? reinterpret_cast<T *>(__get_storage(var))
: nullptr);
}
/* tests */
int main()
{
variant<int, char *> var_test(42);
int * value1 = get_if<int>(var_test);
if (value1)
{
std::cout << *value1 << "\n"; // OK
}
var_test = _strdup("Hello !");
char ** value2 = get_if<char *>(var_test);
if (value2)
{
std::cout << *value2 << "\n"; // OK
}
variant<int, std::string> var_test_2(42);
//var_test_2 = _strdup("Hello again !");
var_test_2 = std::string("Hello again !");
std::string * value3 = get_if<std::string>(var_test_2);
if (value3)
{
std::cout << *value3 << "\n";
}
return 0; // it crashes when the destructor of var_test_2 is called
}
Ich habe ein vollständiges Codebeispiel, sondern nur die Umsetzung von variant
ist wichtig.
Ich nehme an, ich habe die std::aligned_union
schlecht verwendet, aber ich kann nicht herausfinden, wo und wie.
ich den Fehler:
Exception thrown at 0x00096370 in Test.exe: 0xC0000005: Access violation writing location 0xDDDDDDDD.
Update: es scheint der destructor zweimal genannt, aber ich finde nicht, warum.
Was sind Ihre Probleme mit der Zerstörung? – Carcigenicate
@Carcigenicate aktualisiert – Boiethios
> Ich versuche, die Basis von Std :: Variante neu schreiben, weil ich einen C++ 14 Compiler verwenden muss: Verwenden Sie boost :: variant? –