-
August 27th, 2014, 09:47 AM
#16
Re: Is this guaranteed to be correct?
Originally Posted by OReubens
Also, I'm not 100% sure the id's will be guaranteed identical for types originating from separately compiled units.
a conforming compiler should guarantee that ( as long as some requirements are fullfilled, see post#3 )
Originally Posted by JohnW@Wessex
What I my class is designed to do is this.
as suggested in post #9, you can avoid the NextUniqueId() function calls, at the cost of either a (possibly small) runtime or space penalty. For example, something like ( c++03 compatible, off the top of my head solution, not tested ! ) :
Code:
template< typename T1, typename T2 >
struct SameType { static const bool value = false; };
template< typename T >
struct SameType<T,T> { static const bool value = true; };
struct NoType {};
template< typename T, typename T1 = NoType, typename T2 = NoType, typename T3 = NoType > // ... up to some Tmax
struct TypeMap
{
// return the position, or -1 if not found
static const unsigned value = SameType<T1,NoType>::value ? -1 : ( SameType<T,T1>::value ? 0 : ( 1 + TypeMap<T,/*T1,*/T2,T3>::value ) );
};
template< typename T1 = NoType, typename T2 = NoType, typename T3 = NoType > // ... up to Tmax
struct Any
{
unsigned id;
// ...
template < typename U1, typename U2, typename U3 >
bool IsSameType( Any<U1,U2,U3> const& other )
{
// the compiler should be nice enough to optimize obvious branches and apply lookup/binary search whenever appropriate
switch( other.id )
{
case 0: return id == TypeMap<U1,T1,T2,T3>::value;
case 1: return id == TypeMap<U2,T1,T2,T3>::value;
case 2: return id == TypeMap<U3,T1,T2,T3>::value;
default:return false;
}
}
};
or
Code:
template< typename A1, typename A2 >
struct AnyCompare { };
template< typename T1, typename T2, typename T3, typename U1, typename U2, typename U3 >
struct AnyCompare< Any<T1,T2,T3>, Any<U1,U2,U3> >
{
static const unsigned Map[] =
{
TypeMap<U1,T1,T2,T3>::value,
TypeMap<U2,T1,T2,T3>::value,
TypeMap<U3,T1,T2,T3>::value
};
};
// ... possibly more specializations to compress size ( otherwise, for each Any instantiation you'd end up with a Tmax-sized static lookup table )
// ...
template < typename U1, typename U2, typename U3 >
bool IsSameType( Any<U1,U2,U3> const& other )
{
return id == AnyCompare< Any<T1,T2,T3>, Any<U1,U2,U3> >::Map[ other.id ];
}
};
the first code has no space overhead and a runtime overhead depending on how much the compiler optimzes the switch, whereas the second code has no runtime overhead but a per-instantiation space overhead varying from 1 to Tmax times your current solution, depending on the AnyCompare specialization.
Note that you can use the (boost)preprocessor(library) to generate the Tmax dependent boilerplate code ...
-
August 27th, 2014, 10:09 AM
#17
Re: Is this guaranteed to be correct?
Thanks for that.
I'll give it a look over this evening to see if it can be grafted into what I already have.
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
-
August 27th, 2014, 10:27 AM
#18
Re: Is this guaranteed to be correct?
Originally Posted by superbonzo
BTW, in "any1.IsSameTypeAs(any3);" given that you know both the type of any1 and any3 you could simply compare the index of the type as declared in the Any<> specialization, eg
Any<char, short, int> defines a type map f1: char->0, short->1, int->2, let's call id1 the current id
Any<int, std::string> defines a type map f2: int->0, string>1, let's call id2 the current id
the two compare equal iff id1 == f1(f2^-1(id2)). So, no need of the GetTypeIdFor() machinery (unless I missed something )
I've just realised the small, but significant difficulty with that method.
Any<int, double> any1 = 1;
Any<double, int> any2 = 1;
bool b = any1.IsSameTypeAs(any2); // This will return 'false' despite both objects storing an 'int'.
The purpose of GetTypeIdFor() is to always return the same id for a particular type.
I could of course disallow comparison of types from different Any template declarations.
This is probably a reasonable restriction to make in hind-site.
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
-
August 27th, 2014, 10:36 AM
#19
Re: Is this guaranteed to be correct?
Originally Posted by JohnW@Wessex
bool b = any1.IsSameTypeAs(any2); // This will return 'false' despite both objects storing an 'int'.
uhm, I'd say no, it will return true instead. The type map machinery in my previous post will exactly compensate for the types reordering ( the composition f1 o f2^-1 as in the example above ).
IOW,
Any<int, double> any1 = 1; // type map f1: int->0, double->1, id = 0
Any<double, int> any2 = 1; // type map f2: double->0, int->1, id = 1
the comparison reads 0 == ( 1 --f2^-1--> int --f1--> 0 ), that is true
Last edited by superbonzo; August 27th, 2014 at 10:38 AM.
Reason: added IOW
-
August 27th, 2014, 11:10 AM
#20
Re: Is this guaranteed to be correct?
Originally Posted by superbonzo
uhm, I'd say no,
I've only had a chance to briefly skim your code so I probably misunderstood your method
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
-
August 28th, 2014, 06:17 AM
#21
Re: Is this guaranteed to be correct?
@superbonzo
I had a chance to look at your suggestions this morning, and, like a lot of good ideas, its seems obvious once you know the solution.
With a couple of minor changes it grafted in perfectly
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
-
August 28th, 2014, 07:11 AM
#22
Re: Is this guaranteed to be correct?
it may work... it just appears like an even more elaborate runtime typecheck when (unless I'm missing something) everything seems statically typed.
I originally misunderstood your approach as trying to making something akin to a tuple, but you're looking for something akin to a variant.
Why not have your any:
Hold a type indicating which type is being stored
a Union (assuming it's all POD's) or tuple (if complex types) for the actual data.
.
Then rather than dynamically determining an ID at runtime (causing it to be a variable), have your class require a template specialization for the type id.
You'll have to explicitely write the specialisations and take care of making the id's unique yourself, but it'll reduce the 'is same type' check to a byte/byte (or int/int) comparison.
quick hack. it works, it could be prettier
Code:
// Definition for generic template for getTypeId() (do not provide a generic implementation)
template <typename T>
int getTypeId();
// specializations make sure all return values are unique
template <> int getTypeId<int>() { return 1; }
template <> int getTypeId<char>() { return 2; }
template <> int getTypeId<double>() { return 3; }
template <> int getTypeId<bool>() { return 4; }
// add more here.
template <typename T1, typename T2, typename T3>
class Any
{
public:
int storageType;
Any& operator=(T1 t) { storageType=getTypeId<T1>(); t1=t; return *this; }
Any& operator=(T2 t) { storageType=getTypeId<T2>(); t2=t; return *this; }
Any& operator=(T3 t) { storageType=getTypeId<T3>(); t3=t; return *this; }
template <typename OtherT1,typename OtherT2, typename OtherT3>
bool IsSame(const Any<OtherT1,OtherT2,OtherT3>& other) { return storageType==other.storageType; }
union { T1 t1; T2 t2; T3 t3; };// or tuple/struct
};
int main()
{
Any<int, char, double> a1;
a1=1;
Any<double, int, bool> a2;
a2=2;
bool b1 = a1.IsSame(a2); // true (int & int)
a1=4.0;
bool b2 = a1.IsSame(a2); // false (double & int)
a2=6.0;
bool b3 = a1.IsSame(a2); // true (double & double)
return 0;
}
-
August 28th, 2014, 07:16 AM
#23
Re: Is this guaranteed to be correct?
if you really need the id's to be unique without programmer intervention (you'll still need to define all the specialisations)
then you could use one of the compiler macro's as a id (assuming all specialisations are in 1 source file)
such as __LINE__ or you could use __COUNTER__
-
August 28th, 2014, 07:53 AM
#24
Re: Is this guaranteed to be correct?
well, the "make sure all return values are unique", the "add more here" and the "you'll still need to define all the specialisations" parts look like a no-go as far as the OP is concerned. As far as I got it, this is exactly what he wanted to avoid in the first place.
Actually, I think there is a better way, by defining
Code:
template < typename T >
struct Type
{
static inline void* Id() { return reinterpret_cast<void*>(Id); }
};
and using void* ( or a convertible type thereof ) as an identifier in the Any instance, it should work ok ( also thread safe in c++03 ) and the linker should be smart enough to eliminate any space overhead from the final executable.
-
August 28th, 2014, 07:58 AM
#25
Re: Is this guaranteed to be correct?
Originally Posted by OReubens
Why not have your any:
Hold a type indicating which type is being stored
a Union (assuming it's all POD's) or tuple (if complex types) for the actual data.
Then rather than dynamically determining an ID at runtime (causing it to be a variable), have your class require a template specialization for the type id.
That's actually very similar to what I already had, except that instead of a union I have a data area sized to the largest type (and aligned to a 32bit boundary)
When a value is stored it uses a 'placement new' to copy construct the value in the data area.
Using superbonzo's technique, the type ids are now defined as constants at compile time rather than assigning them from a static function & variable at runtime. The only per-instance RAM overhead I have is one byte for the stored type id plus the difference in size between the largest and stored type.
, but it'll reduce the 'is same type' check to a byte/byte (or int/int) comparison.
As the type ids are contiguous integers the switch->case->byte/const byte comparison is likely to be pretty efficient.
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
-
August 28th, 2014, 08:13 AM
#26
Re: Is this guaranteed to be correct?
Originally Posted by superbonzo
Actually, I think there is a better way,
That's an interesting idea, though I still have the niggling doubt in the back of my mind that there may be the possibility of getting more than one instance of Id() for a particular type. I could be wrong.
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
-
August 28th, 2014, 08:28 AM
#27
Re: Is this guaranteed to be correct?
... and I have some doubts concerning the portability of the comparison of the so obtained void* in this case ... and even if the code were perfectly legal and guaranteed, I'd be not surprised to discover that some compilers/linkers doesn't like it anyway. So, yes, kind of risky without some further investigation ...
-
August 29th, 2014, 12:57 AM
#28
Re: Is this guaranteed to be correct?
Originally Posted by JohnW@Wessex
I'm experimenting with a class similar in some ways to Boost::any, but more resource and performance friendly to our embedded platform.
Again, one of the most prominent characteristics of C++ is its type safety. It's amazing how much effort is spent on giving that away for nothing. It would be much better if all this effort went into making good solid designs instead.
Boost::Any is a bad idea from start to finish. If people cannot design without downcasting then they should be forced to do so explicitly so it can be detected and corrected at the next code review. So for the sake of your platform I hope you drop this malign "feature".
Last edited by razzle; August 29th, 2014 at 02:36 AM.
-
August 29th, 2014, 08:30 AM
#29
Re: Is this guaranteed to be correct?
Originally Posted by superbonzo
Actually, I think there is a better way, by defining
Yes, I did think of something like this too but... It won't work if you're dealing with multiple loadable modules (such as dll's) (yes, occupational thing, I'm always concerned about how code behaves if multiple dll's come to play :-))
-
August 29th, 2014, 08:33 AM
#30
Re: Is this guaranteed to be correct?
Originally Posted by JohnW@Wessex
That's an interesting idea, though I still have the niggling doubt in the back of my mind that there may be the possibility of getting more than one instance of Id() for a particular type. I could be wrong.
Well... if yes, then your idea won't work either.
This is a function, so is your constructor approach in #1.
So if this fails, your #1 would fail in the exact same way.
I can confirm that if you're dealing with dll's or libs, that this approach is indeed not guaranteed, depending on how you export things from the dll or lib.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|