I have the following class:
template <typename Type = void>
class AlignedMemory {
public:
AlignedMemory(size_t alignment, size_t size)
: memptr_(0) {
int iret(posix_memalign((void **)&memptr_, alignment, size));
if (iret) throw system_error("posix_memalign");
}
virtual ~AlignedMemory() {
free(memptr_);
}
operator Type *() const { return memptr_; }
Type *operator->() const { return memptr_; }
//operator Type &() { return *memptr_; }
//Type &operator[](size_t index) const;
private:
Type *memptr_;
};
And attempt to instantiate an automatic variable like this:
AlignedMemory blah(512, 512);
This gives the following error:
src/cpfs/entry.cpp:438: error: missing template arguments before ‘blah’
What am I doing wrong? Is void not an allowed default parameter?
I think that you need to write:
AlignedMemory<> blah(512, 512);
See 14.3 [temp.arg] / 4:
When default template-arguments are used, a template-argument list can be empty. In that case the empty <> brackets shall still be used as the template-argument-list.
Your syntax is wrong:
AlignedMemory blah(512, 512); //wrong syntax
Correct syntax is this:
AlignedMemory<> blah(512, 512); //this uses "void" as default type!
The error message itself gives this hint. Look at it again:
src/cpfs/entry.cpp:438: error: missing template arguments before
‘buf’
PS: I'm sure 'buf' is a typo. You wanted to write 'blah' - the name of your variable!
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 couple of A like classes with method, say, get_value(), which return values of different types. And one class B, which returns a value with method value(). For example:
struct A_like1 {
int get_value() const { return 10; }
};
struct A_like2 {
char get_value() const { return 'a'; }
};
struct B
{
float value() const { return 2.5; }
};
Now, I need a function to retrieve that value for these classes. I have a function:
template<typename T>
auto get_value(const T & t) -> result_of<decltype(&T::get_value)(T)>::type
{
return t.get_value();
}
It gives me a number of compilation errors on VS2010 starting with:
Error 1 error C2061: syntax error : identifier 'type' c:\_data\work\vmxi\c++\vox\vox\template_specialization.h 51
For B I have an overload, which works fine. The question is, how to make get_value() work with result_of<>()?
P.S. Hmm, I have just realized that I could've used -> decltype(T().get_value()) instead, which works fine. But why doesn't result_of<>() work? Besides, I was able to declare a variable in .cpp file:
result_of<decltype(&A_like1::get_value)(A_like1)>::type i=0;
The keyword typename can be used to declare that a dependent name, like std::result_of<T>::type, is a type. Otherwise, I believe it's assumed that std::result_of<T>::type is a data member. In C++14, several type traits have gotten a using alias that includes the typename keyword for you. These alias are always have the same name as the trait, with a _t suffix.
Try :
typename result_of<decltype(&T::get_value)(T)>::type
or, with C++14 :
result_of_t<decltype(&T::get_value)(T)>
VisualStudio 2008 (VC++ 9)
Problem with overloading operator()
Hello community!
It seems that overloaded version of operator() must all differ in their argument list, independly of the return type.
I have the following case:
class Sha256{
public:
QVector<quint32> operator()(QString const& data);
QByteArray operator()(QByteArray const& data);
QVector<quint32> operator()(QByteArray const& data); // this is line 168
...
}
Last declaration leads to following compilation error:
.\sha256.cpp(168) : error C2556: 'QVector Sha256::operator ()(const QByteArray &)' : overloaded function differs only by return type from 'QByteArray Sha256::operator ()(const QByteArray &)'
with
[
T=uint
]
On the other side, following declarations are working:
class Sha256{
public:
QVector<quint32> operator()(QString const& data);
QByteArray operator()(QByteArray const& data);
...
}
because they have different arguments.
One way would be to add in the first case an additional argument like
QByteArray<quint32> operator()(QByteArray const& data, bool getByteArray)
but this is embarassing, not intuitive and also not clean (return type would be a QByteArray, independly of the value of getByteArray, even in case of false!).
Another way could be to use a template function like
template<class T> T operator()(QByteArray const& ba)
template<class T> T operator()(QString const& str)
but T could only be of a few 'supported' type (QByteArray and QVector), therefore user could run into trouble if using wrong T Type. This version seems to be error prone.
So my questions are:
1. Why is the return type of function objects not taken into account in the signature to distinguish between different flavors of the function like in 'classical' functions?
2. Is there a better way around than adding an additional dummy argument?
Thank you for your valuable time.
Alain
Return type is never used for overload resolution. In fact, you can't even declare two functions that differ only in return type. In my opinion, adding this feature would be complicated and not that helpful. Is it necessary to use the function call operator? You could just use a named member function, and then give them different names. Another option would be to use a member template, but even then you would need to explicitly give the return type as a template argument, and the syntax would be ugly if done on the function call operator.
The comment from Kerrek SB suggests a workaround by leveraging conversion operators, if you are okay with something that is complex on the inside, but simpler on the outside.
#include <iostream>
struct A {};
struct B {};
A fooA_impl(const char *) {
std::cerr << "A impl" << std::endl;
return A();
}
B fooB_impl(const char *) {
std::cerr << "B impl" << std::endl;
return B();
}
class Helper {
public:
operator A() { return fooA_impl(p); }
operator B() { return fooB_impl(p); }
private:
friend Helper foo(const char *p);
Helper(const char *p_) : p(p_) {}
const char *const p;
};
Helper foo(const char *p) {
return Helper(p);
}
int
main() {
A a(foo("hello"));
B b(foo("hello"));
}
You can't use overload by return type. One thing you can do is to add the return type as a tag parameter when overloading operator():
QByteArray operator()(QByteArray const& data, const QByteArray& tag );
QVector<quint32> operator()(QByteArray const& data, const QVector<quint32>& tag);
The below is how you call these function:(assume both types have default constructors).
(data, QByteArray());
(data, QVector<quint32>());
return Graph_iterator_Vertex(Vertexs.begin());
I do not understand why this line is called the copy constructor, although there is my constructor with a parameter. The arguments constructor parameter written specifically for this design.
All that I have found about a similar issue is the use of typename. But it did not solve my problem.
struct Graph_iterator_Vertex
{
private:
typename std::vector<Vertex<Type_Of_Value_Vertex, Type_Of_Value_Edge>>::iterator iterator;
public:
Graph_iterator_Vertex(typename std::_Vector_const_iterator<std::vector<Vertex<Type_Of_Value_Vertex, Type_Of_Value_Edge>>> iterator_)
{
iterator=iterator_
}
};
const Graph_iterator_Vertex begin(void) const
{
return Graph_iterator_Vertex(Vertexs.begin());
}
where
std::vector<Vertex<Type_Of_Value_Vertex, Type_Of_Value_Edge>> Vertexs;
Error:
error C2440: <function-style-cast>: can not be converted std::_Vector_const_iterator<_Myvec>"in "Graph<Type_Of_Value_Vertex,Type_Of_Value_Edge>::Graph_iterator_Vertex"
you should use
std::vector<Vertex<Type_Of_Value_Vertex, Type_Of_Value_Edge>>::const_iterator
instead of
std::_Vector_const_iterator<std::vector<Vertex<Type_Of_Value_Vertex, Type_Of_Value_Edge>>>
as the type of your function parameter.
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.