|
|
Home » U++ Library support » U++ Core » How to mark std::array<T, N> moveable if only T is moveable
How to mark std::array<T, N> moveable if only T is moveable [message #60668] |
Tue, 02 July 2024 17:54  |
busiek
Messages: 70 Registered: February 2011 Location: Poland
|
Member |
|
|
Hi all,
U++ does not have a container with std::array functionality. Is there any simple way to mark std::array<T, N> moveable if only T is moveable?
I didn't come with anything better than implementing my own version of std::array derived from Moveable<MyArray<T, N>>. By using macro NTL_MOVEABLE one can only mark moveable a specific type not a template. Even if I want to mark specific template type, macro does not work with template types having two or more parameters and I have to expand macro myself:
template<> inline void AssertMoveable<std::array<double, 2>>(std::array<double, 2>*) {}
Correct me if I miss something. Shouldn't the mechanism of marking type moveable be done through partial class instantiation? Or concepts with C++20?
|
|
|
|
|
|
|
|
Re: How to mark std::array<T, N> moveable if only T is moveable [message #60689 is a reply to message #60688] |
Fri, 19 July 2024 13:52   |
Lance
Messages: 656 Registered: March 2007
|
Contributor |
|
|
mirek wrote on Fri, 19 July 2024 07:11Same semantics does not cover U++ moveable. Now I agree it is unlikely, but so I thought about std::string until I met implementation that is not U++ moveable...
I looked into std::basic_string in this post, message #59178.
Quote:
Story of std::basic_string (GLIBCXX implementation)
It's surprising that a basic_string<ch> would cause trouble (core dump etc) when treated as raw bytes. Digging into its implementation (in <bits/basic_string.h>), we have the data members
... .
On the same post, I proposed to use class traits to handle Moveable. There are details I could not iron out. Also, it might not be compatible with very old c++ because I have an added goal to allow non-trivially relocatable class objects, which can be corrected pre- or post relocation, be housed in std::vector or upp::Vector.
|
|
|
|
|
|
|
Re: How to mark std::array<T, N> moveable if only T is moveable [message #60696 is a reply to message #60682] |
Tue, 23 July 2024 18:25   |
busiek
Messages: 70 Registered: February 2011 Location: Poland
|
Member |
|
|
mirek wrote on Thu, 18 July 2024 08:41Lance wrote on Wed, 03 July 2024 03:39Reminds me of some exploration I did with Moveable.
I vaguely recalled that when I tested a few years back, the way Moveable was designed were causing a compile time error with Windows/VSBuild. I would also welcome a redesign of Moveable using more recent language facilities.
Suggestions? (But it needs to be backward compatible).
I was thinking about something like that:
#include <Core/Core.h>
#include <type_traits>
namespace Upp {
// First way of marking class moveable is to be an ancestor of NtlMoveableBase
template <class T> class NtlMoveableBase {};
// Second way is to specialize NtlMoveableClass
template <class T>
struct NtlMoveableClass
: std::integral_constant<bool,
std::is_trivial_v<T>
|| std::is_base_of_v<Upp::NtlMoveableBase<T>, T>
// below is not needed if we make them derived from NtlMoveableBase
|| std::is_base_of_v<Upp::Moveable_<T>, T>
|| std::is_base_of_v<Upp::Moveable<T>, T>
|| std::is_base_of_v<Upp::MoveableAndDeepCopyOption<T>, T>> {};
// For instance mark std::array<T, N> moveable if only T is moveable
template <class T, size_t N>
struct NtlMoveableClass<std::array<T, N>>
: std::integral_constant<bool, NtlMoveableClass<T>::value> {};
// Helper for checking whether class is moveable
template <class T>
inline constexpr bool IsNtlMoveable = NtlMoveableClass<T>::value;
// Optional concept for c++20
template <class T>
concept NtlMoveable = IsNtlMoveable<T>;
}
using namespace Upp;
// use of concept
template <NtlMoveable T>
struct MyVector
{
T *ptr;
// Or use static_assert
~MyVector() { static_assert(IsNtlMoveable<T>); }
};
CONSOLE_APP_MAIN
{
// Checking if given type is moveable
static_assert(IsNtlMoveable<int>);
static_assert(IsNtlMoveable<const void *>);
static_assert(IsNtlMoveable<Vector<int>>);
static_assert(IsNtlMoveable<std::array<int, 5>>);
static_assert(IsNtlMoveable<std::array<Vector<int>, 5>>);
static_assert(!IsNtlMoveable<Thread>);
}
You could redefine NTL_MOVEABLE macro as a specialization of NtlMoveableClass.
But you need to change an assertion AssertMoveable((T*)0) to static_assert(IsNtlMoveable<T>).
Are those ideas usable?
|
|
|
Re: How to mark std::array<T, N> moveable if only T is moveable [message #60700 is a reply to message #60696] |
Thu, 25 July 2024 02:27   |
 |
mirek
Messages: 14255 Registered: November 2005
|
Ultimate Member |
|
|
busiek wrote on Tue, 23 July 2024 18:25mirek wrote on Thu, 18 July 2024 08:41Lance wrote on Wed, 03 July 2024 03:39Reminds me of some exploration I did with Moveable.
I vaguely recalled that when I tested a few years back, the way Moveable was designed were causing a compile time error with Windows/VSBuild. I would also welcome a redesign of Moveable using more recent language facilities.
Suggestions? (But it needs to be backward compatible).
I was thinking about something like that:
#include <Core/Core.h>
#include <type_traits>
namespace Upp {
// First way of marking class moveable is to be an ancestor of NtlMoveableBase
template <class T> class NtlMoveableBase {};
// Second way is to specialize NtlMoveableClass
template <class T>
struct NtlMoveableClass
: std::integral_constant<bool,
std::is_trivial_v<T>
|| std::is_base_of_v<Upp::NtlMoveableBase<T>, T>
// below is not needed if we make them derived from NtlMoveableBase
|| std::is_base_of_v<Upp::Moveable_<T>, T>
|| std::is_base_of_v<Upp::Moveable<T>, T>
|| std::is_base_of_v<Upp::MoveableAndDeepCopyOption<T>, T>> {};
// For instance mark std::array<T, N> moveable if only T is moveable
template <class T, size_t N>
struct NtlMoveableClass<std::array<T, N>>
: std::integral_constant<bool, NtlMoveableClass<T>::value> {};
// Helper for checking whether class is moveable
template <class T>
inline constexpr bool IsNtlMoveable = NtlMoveableClass<T>::value;
// Optional concept for c++20
template <class T>
concept NtlMoveable = IsNtlMoveable<T>;
}
using namespace Upp;
// use of concept
template <NtlMoveable T>
struct MyVector
{
T *ptr;
// Or use static_assert
~MyVector() { static_assert(IsNtlMoveable<T>); }
};
CONSOLE_APP_MAIN
{
// Checking if given type is moveable
static_assert(IsNtlMoveable<int>);
static_assert(IsNtlMoveable<const void *>);
static_assert(IsNtlMoveable<Vector<int>>);
static_assert(IsNtlMoveable<std::array<int, 5>>);
static_assert(IsNtlMoveable<std::array<Vector<int>, 5>>);
static_assert(!IsNtlMoveable<Thread>);
}
You could redefine NTL_MOVEABLE macro as a specialization of NtlMoveableClass.
But you need to change an assertion AssertMoveable((T*)0) to static_assert(IsNtlMoveable<T>).
Are those ideas usable?
Yep. After tinkering and simplifying I think this should cover it all:
template <class T> struct Moveable {};
template <class T>
inline constexpr bool is_Moveable = std::is_trivially_copyable<T>::value ||
std::is_base_of<moveable<T>, T>::value;
We will not need NTL_MOVEABLE macro anyway... Trivial types are covered and allowing non-trivial types to be explicitly marked Moveable is outright dangerous.
BTW, std::array passes is_Moveable out of box.
See any problem? (Apart for requiring C++17, but I guess we can go there for the next release)
Mirek
|
|
|
|
Re: How to mark std::array<T, N> moveable if only T is moveable [message #60702 is a reply to message #60701] |
Thu, 25 July 2024 09:19   |
 |
mirek
Messages: 14255 Registered: November 2005
|
Ultimate Member |
|
|
Lance wrote on Thu, 25 July 2024 03:16
The capability to manually mark a class might still be an asset. Suppose we receive a third party class whose objects are trivially relocatable but not trivially copyable, the simplest way to make Upp::Vector accept it is to mark it. Wrongfully marked class will usually result in immediate runtime error, hence should not be a big concern.
I disagree. They might be relocatable in that version of library with given compiler only. Immediate runtime error might happen when you do testing, but developer might not be even aware he is supposed to test it.
Also from practical point of view, this never happened 
What might be usefull though is the capability to manually mark a class to be relocated by regular move/copy constructor:
- I still think it is a good idea not to do move/copy as default option to force effectivness
- but this would allow things like Vector<std::string> with some performance penalty...
Another thing to consider: Tuple is moveable if all its components are moveable. Are we able to express that somehow? (If not, no big deal, I can make Tuple moveable and request that elements are).
Mirek
[Updated on: Thu, 25 July 2024 09:33] Report message to a moderator
|
|
|
Re: How to mark std::array<T, N> moveable if only T is moveable [message #60703 is a reply to message #60702] |
Thu, 25 July 2024 09:32   |
 |
mirek
Messages: 14255 Registered: November 2005
|
Ultimate Member |
|
|
mirek wrote on Thu, 25 July 2024 09:19
Another thing to consider: Tuple is moveable if all its components are moveable. Are we able to express that somehow? (If not, no big deal, I can make Tuple moveable and request that elements are).
Figured it out:
struct not_moveable {};
template <class A, class B>
class Two : std::conditional<is_moveable<A> && is_moveable<B>, moveable<Two<A, B>>, not_moveable>::type {
A a;
B b;
};
[Updated on: Thu, 25 July 2024 09:32] Report message to a moderator
|
|
|
|
Re: How to mark std::array<T, N> moveable if only T is moveable [message #60706 is a reply to message #60703] |
Thu, 25 July 2024 17:32   |
busiek
Messages: 70 Registered: February 2011 Location: Poland
|
Member |
|
|
mirek wrote on Thu, 25 July 2024 09:32mirek wrote on Thu, 25 July 2024 09:19
Another thing to consider: Tuple is moveable if all its components are moveable. Are we able to express that somehow? (If not, no big deal, I can make Tuple moveable and request that elements are).
Figured it out:
struct not_moveable {};
template <class A, class B>
class Two : std::conditional<is_moveable<A> && is_moveable<B>, moveable<Two<A, B>>, not_moveable>::type {
A a;
B b;
};
But what if one wants to use std::tuple instead of Upp::Tuple? How to mark it moveable if tuple components are moveable?
I sometimes prefer to use std::tuple because one can write:
auto [a, b] = some std::tuple
Alternatively, there is a way to implement Upp::Tuple that it works with structured binding?
|
|
|
Re: How to mark std::array<T, N> moveable if only T is moveable [message #60707 is a reply to message #60704] |
Fri, 26 July 2024 00:00   |
Lance
Messages: 656 Registered: March 2007
|
Contributor |
|
|
mirek wrote on Thu, 25 July 2024 04:07
// For instance mark std::array<T, N> moveable if only T is moveable
template <class T, size_t N>
struct NtlMoveableClass<std::array<T, N>>
: std::integral_constant<bool, NtlMoveableClass<T>::value> {};
BTW, easiest and really nice way how to add optional trait:
template <> inline constexpr bool is_moveable<std::string> = true;
(do not want this, but could be useful to know)
Exactly, like it or not, a programmer can mark a class as moveable.
Another not very convincing example.
#include <iostream>
#include <type_traits>
#include <array>
template <class T> struct Moveable {};
template <class T>
inline constexpr bool is_moveable_v = std::is_trivially_copyable<T>::value ||
std::is_base_of<Moveable<T>, T>::value;
class C: public Moveable<C>{
C(const C& c){}
C(C&& c){}
};
// uncomment to communicate the moveability of array<T,n> to the compiler
//template <class T, int n>
//inline constexpr bool is_moveable_v<std::array<T,n>> = is_moveable_v<T>;
int main()
{
std::cout<<is_moveable_v<std::array<C,5>><<std::endl;
}
|
|
|
Re: How to mark std::array<T, N> moveable if only T is moveable [message #60714 is a reply to message #60703] |
Mon, 29 July 2024 08:47   |
 |
mirek
Messages: 14255 Registered: November 2005
|
Ultimate Member |
|
|
mirek wrote on Thu, 25 July 2024 09:32mirek wrote on Thu, 25 July 2024 09:19
Another thing to consider: Tuple is moveable if all its components are moveable. Are we able to express that somehow? (If not, no big deal, I can make Tuple moveable and request that elements are).
Figured it out:
struct not_moveable {};
template <class A, class B>
class Two : std::conditional<is_moveable<A> && is_moveable<B>, moveable<Two<A, B>>, not_moveable>::type {
A a;
B b;
};
Nicer and simpler way:
template <class A, class B> inline constexpr bool is_moveable<Two<A, B>> = is_moveable<A> && is_moveable<B>;
I am about ready to try to introduce this into U++, but I am actually out of ideas how to name things....
Moveable / is_moveable is not perfect (future c++ might name this trivially_relocatable, but that is too long IMO), but u++ traditional, I guess I can live that.
However, new system should introduce a trait that says "I know I cannot memmove this type, but I still would like to store it into Vector anyway". E.g. to have Index<std::string> ... Really not sure what id should I use for that... Something like "is_vector_compatible_but_slower", or "std_moveable" or something?
(alternative would be to have "mem moveable" as option, but I want to force developer to be explicit, not to forget to mark moveable types)
Mirek
|
|
|
Goto Forum:
Current Time: Mon Apr 28 15:30:54 CEST 2025
Total time taken to generate the page: 0.00879 seconds
|
|
|