I have error in compile time
INLINE template<typename T> T *&Animation::GetKey(int subAnim, int node, int frameInSubAnim)
{
const int keyIndex = GetKeyIndex(subAnim, node, frameInSubAnim);
return static_cast<T*>(m_Keys[keyIndex]);
}
With following error
d:\before_me\motion\pipeline\animation\AnimationData.inl(98):
error C2440: 'return' : cannot convert from 'Motion::Animation::Key *' to 'Motion::Animation::Key *&'
and how i can workaround it?
The compiler is telling you that the static_cast<T*>(...) yields a temporary (rvalue) and that cannot be bound by a non-const reference (return type is T*&). Note that even it it would bind to T*const& you don't really want that.
It is not clear what you are trying to achieve, but consider returning T* (drop the reference).
I think this captures what you want and provides a hideous workaround
void* m_keys[] = { 0, 0, 0 };
template<typename T>
T*& foo(const int index)
{
return *reinterpret_cast<T**>(&m_keys[index]);
}
int main()
{
foo<int>(0) = new int();
}
Related
#include <variant>
#include <iostream>
typedef unsigned char uint8_t;
enum class typeFlag {
kFloat32 = 0,
kFloat64 = 1,
kUint8 = 2
};
class NDArray;
template<typename T>
class TBlob{
friend class NDArray;
public:
TBlob(): dptr_(nullptr) {};
TBlob(size_t size): dptr_(new T[size]) {};
~TBlob(){if (dptr_) delete [] dptr_;}
private:
T *dptr_;
};
using datahandle = std::variant<TBlob<float> *, \
TBlob<double> *, \
TBlob<uint8_t> * >;
class NDArray{
public:
NDArray(): dtype_(typeFlag::kFloat32) {}
NDArray(size_t size, typeFlag dtype): dtype_(dtype) {alloc(size);}
~NDArray() {if (&data_) delete &data_;}
void alloc(size_t size){
switch(dtype_){
case typeFlag::kFloat32:
data_ = new TBlob<float>(size);
break;
case typeFlag::kFloat64:
data_ = new TBlob<double>(size);
break;
case typeFlag::kUint8:
data_ = new TBlob<uint8_t>(size);
break;
default:
data_ = new TBlob<float>(size);
break;
}
}
const auto getData() {
if (dtype_ == typeFlag::kFloat32) return std::get<0>(data_)->dptr_;
else if (dtype_ == typeFlag::kFloat64) return std::get<1>(data_)->dptr_;
else return std::get<2>(data_)->dptr_;
}
private:
typeFlag dtype_;
datahandle data_;
};
int main(){
NDArray a(5,typeFlag::kUint8);
std::cout << a.getData()[0] << std::endl;
return 0;
}
Got Error:
prog.cc: In member function 'const auto NDArray::getData()':
prog.cc:52:79: error: inconsistent deduction for auto return type: 'float* const' and then 'double* const'
else if (dtype_ == typeFlag::kFloat64) return std::get<1>(data_)->dptr_;
^~~~~
prog.cc:53:45: error: inconsistent deduction for auto return type: 'float* const' and then 'unsigned char* const'
else return std::get<2>(data_)->dptr_;
^~~~~
Seems that the getData() function always return a float * type according to the default value of dtype_, which is float.
How can I make it possible to return different type according to the run-time value of dtype_? I need to process the pointer returned by getData() and those operations is unrelated to the type of this pointer. For example:
NDArray NDArray::operator*(const float scalar) const{
NDArray ret(*this);
for (size_t i=0; i<ret.getSize(); i++){
ret.getData()[i] *= scalar;
}
return ret;
}
// Many other operations...
If I template getData(), does it means that I must template operator* too? It will make things ugly and I want to avoid it.
If you have better idea, plz help me!
A single non-template function (or class member) cannot have a return type dependent on some runtime value (dtype_ in your case), that's the core of the error. The compiler tries to deduce a single return type and fails, since different code paths return float *, double * and uint8_t *.
It isn't true that your operations don't depend on the type of the pointer. In this single line ret.getData()[i] *= scalar you already need to know the type, for proper indexing (the [i] part) and proper multiplication (the *= part).
There are many options for what you could do here. I would suggest to make NDArray a template itself that would hold a data pointer & a size, without any type flags. This would make things much simpler & future-proof (e.g. nothing breaks if you decide that you also need to store uint16_t).
If, however, you absolutely must have this 3-type layout with runtime deduction of the type, you could do roughly the following. Add an apply function that will take any callable object and call it with an appropriate data type, e.g.
class NDArray {
...
template <typename F>
void apply(F f) {
if (dtype_ == typeFlag::kFloat32)
f(std::get<0>(data_)->dptr_, getSize());
else if (dtype_ == typeFlag::kFloat64)
f(std::get<1>(data_)->dptr_, getSize());
else
f(std::get<2>(data_)->dptr_, getSize());
}
...
};
and then use it in your operator * with a template lambda:
NDArray NDArray::operator*(const float scalar) const{
NDArray ret(*this);
ret.apply([](auto * data, size_t size){
for (size_t i=0; i<size; i++){
data[i] *= scalar;
}
});
return ret;
}
Note that this apply copies the callable for simplicity, but it should rather accept a forwarding reference and do proper forwarding, like
template <typename F>
void apply(F && f) {
...
std::forward<F>(f)(std::get<0>(data_)->dptr_, getSize());
...
}
There is no way to do this from a single function, since the return type is not part of a function's signature. I.e. int foo (); and double foo (); are the same function, so can't exist at the same time.
Since your code is using a variant internally already, you could design your access functions to work similar to std::variant. E.g. make the accessor a template:
template <typeFlag Type>
auto const & getData () {
if constexpr (Type == typeFlag::kFloat32) { return std::get<0>(data_)->dptr_; }
else if constexpr (Type == typeFlag::kFloat64) { return std::get<1>(data_)->dptr_; }
else { return std::get<2>(data_)->dptr_; }
}
Of course, this requires the caller to know which template argument to use for getData(). The answer by lisyarus shows a nice way to implement vistor-like behavior instead, which does not require this.
Some extra remarks:
The dtype_ enum is kind of redundant. All necessary type information is already contained within data_. You can get it out using e.g. std::holds_variant<TBlob<float> *>(data_).
You're using raw pointers, which IMHO is just asking for problems. Use std::unique_ptr instead. That will take care of all potential memory leaks and make your NDArray uncopyable.
You should follow the rule of 5: https://en.cppreference.com/w/cpp/language/rule_of_three. You're currently going to be either leaking memory all over the place or calling delete on already deleted pointers.
I would template data_ and remove dtype_:
template <typename TData>
class NDArray
{
public:
...
const TData getData() { return std::get<0>(data_)->dptr_; }
private:
TData data_;
}
Then, when you construct it, pass the type in the template, not as an argument:
int main()
{
NDArray<uint8_t> a(5);
}
If you need to make construction of NDArray dynamic, create a function and use a similar switch to the one you have in alloc() but returning NDArray object:
switch(dtype_)
{
case typeFlag::kFloat32: return NDArray<float>(size);
case typeFlag::kFloat64: return NDArray<double>(size);
case typeFlag::kUint8: return NDArray<uint8_t>(size);
default: return NDArray<float>(size);
}
I have a function to move an int from a storage-ptr:
int&& MoveFromStorage(void const* p)
{
return static_cast<int&&>(*static_cast<int*>(const_cast<void*>(p)));
}
int main()
{
int i = 10;
int&& iRValRef = MoveFromStorage(&i);
}
Compiling this function with MSVC 2017 leads to warning C4172: returning address of local variable or temporary. I do not get this warning with clang or gcc. I do not see where I am returning address of local variable or temporary. Is this a bug in MSVC?
EDIT
Maybe I give some more context: I write a std::variant myself and for the visit-function I write following code:
template<class AlternativeType>
inline AlternativeType _cast_to(void const* variant_storage)
{
typedef typename std::remove_reference<AlternativeType>::type without_ref;
return static_cast<AlternativeType>(*static_cast<without_ref*>(const_cast<void*>(variant_storage)));
}
template<class AlternativeType, class Visitor>
inline auto _visit_impl(Visitor&& vis, void const* variant_storage)
-> decltype(std::forward<Visitor>(vis)(_cast_to<AlternativeType>(variant_storage)))
{
return std::forward<Visitor>(vis)(_cast_to<AlternativeType>(variant_storage));
}
And then I use those functions by filling an array with:
FPtrType arr[] = { _visit_impl<QualifiedCurrentType>... };
whereas QualifiedCurrentType the Ith type of the variant is with the constness and referenceness of the variant applied. I.e. if the variant is passed by r-value-ref, QualifiedCurrentType will be an RvalueRef to some other Type. And for those cases, _cast_to generates the warning.
I can't explain it, but following change helped me to fix the code:
template<class AlternativeType>
inline AlternativeType _cast_to(void const* variant_storage)
{
typedef typename std::remove_reference<AlternativeType>::type without_ref;
without_ref* typedPointer = static_cast<without_ref*>(const_cast<void*>(variant_storage));
return static_cast<AlternativeType>(*typedPointer);
}
So the solution is to store some intermediate typedPointer before applying further castings.
I have a struct with a std::map of pointers inside it. I'm trying to do the following:
template <class T>
struct Foo
{
std::map<std::string, T*> f;
T& operator[](std::string s)
{
return *f[s];
}
}
and then use it like this:
Foo<Bar> f;
f["key"] = new Bar();
but the way it's written, it crashes the program. I also tried like this:
T* operator[](std::string s)
{
return f[s];
}
but it doesnt compile. It says "lvalue required as left operand of assignment" on the f["key"] = new Bar() line.
I expected it to be easy since I'm trying to return a pointer and I'm storing a pointer. What is wrong with my code?
The correct way of doing this is:
T*& operator[](std::string s)
{
return f[s];
}
and call it like f["key"] = new Bar().
EDIT: You should start passing non-basic types by const reference where you can:
T*& operator[](const std::string& s)
I've a problem on this code:
template <typename T>
void dosth(T& value,const T& default_value)
{
if (condition)
value = 10;
else
value = default_value;
}
When I call that with
enum {
SITUATION1,
STIUATION2
};
int k;
dosth(k,SITUATION1);
the compiler (g++ 4.5) says
no matching function for call to 'dosth(int&,)'
Why doesn't the compiler automatically cast the enum into an int?
Your problem is due to the fact that the template cannot be instantiated from the function arguments that you supply. No implicit conversion to int occurs, because there's no function to call at all.
If you cast instead of attempting to rely on implicit conversion, your program will work:
dosth(k, static_cast<int>(SITUATION1));
Or, if you provide the function template's arguments explicitly, then the function argument will be converted implicitly as you expect, and your program will work:
dosth<int>(k, SITUATION1);
Would this be better for enums?
class Situations
{
private:
const int value;
Situations(int value) : value(value) {};
public:
static const Situations SITUATION1() { return 1; }
static const Situations SITUATION2() { return 2; }
int AsInt() const { return value; }
};
Will enable type safety. Then use it to create a type safte template.
i.e. Value for pass or fail.
I haven't used void* and const_correctness before so I am not understanding what I am doing wrong in the below code. All I want is to cast a void* returned by a member function of a const object to int*. Please suggest better approaches. Thank you.
I get the following error
passing 'const MyClass' as 'this' argument of 'void* MyClass::getArr()' discards qualifiers
So here's the actual program that I had problem with
class MyClassImpl{
CvMat* arr;
public:
MyClassImpl(){arr = new CvMat[10];}
CvMat *getArr(){return arr;}
};
class MyClass{
MyClassImpl *d;
public:
const void *getArr()const{ return (void*)d->getArr(); }
};
void print(const MyClass& obj){
const int* ptr = static_cast<const int *>(obj.getArr());
}
int main(){
MyClass obj1;
print(obj1);
}
Only the methods such as 'print()' in this case know the datatype returned by 'getData'. I can't use templates because the user doesn't know how MyClass is implemented. Thank you. Feel free to suggest alternatives.
I think the problem is not in the cast from your array to a void * but in trying to call obj.getArr() when obj is marked const and MyClass::getArr() is not a const member function. If you change your definition of that member function to
const void *getArr() const { return static_cast<const void*>(arr); }
Then this error should resolve itself. You might want to do a const-overload as well:
const void *getArr() const { return static_cast<const void*>(arr); }
void *getArr() { return static_cast< void*>(arr); }