I have an Array class that is inheriting from BaseArray class. In BaseArray, I have the protected member variables data_ and cur_size_. The Array class introduces a resize function. The problem I am encountering is that none of the protected member variables from BaseArray are seeming to be accessed in the resize function.
EDIT: Solved the max_size_ problem, but the cur_size_ and data_ file persists
Inheritance? Scope? Help?
The Error:
In file included from Array.h:41:0,
from driver.cpp:6:
Array.cpp: In member function ‘void Array<T>::resize(size_t)’:
Array.cpp:29:5: error: ‘data_’ was not declared in this scope
data_=data_;
^
Array.cpp:30:18: error: ‘cur_size_’ was not declared in this scope
if (new_size>cur_size_)
^
Array.cpp:37:5: error: ‘cur_size_’ was not declared in this scope
cur_size_=new_size;
^
The Code:
BaseArray.h:
#ifndef _BASEARRAY_H_
#define _BASEARRAY_H_
#include <cstring>
template <typename T>
class BaseArray
{
public:
/// Type definition of the element type.
typedef T type;
//constructors, destructor and methods…
protected:
/// Pointer to the actual data. m
char * data_;
/// Current size of the BaseArray.
size_t cur_size_;
};
#include "BaseArray.inl"
#include "BaseArray.cpp"
#endif // !defined _BASEARRAY_H_
Array.h:
#ifndef _ARRAY_H_
#define _ARRAY_H_
#include <cstring>
#include "BaseArray.h"
template <typename T>
class Array: public BaseArray<T> //inheriting from BaseArray
{
public:
/// Type definition of the element type.
typedef T type;
/// Default constructor.
Array (void);
Array (const Array & arr);
/// Destructor.
~Array (void);
const Array & operator = (const Array & rhs);
void resize (size_t new_size);
private:
size_t max_size_; //introduces max_size
};
#include "Array.inl"
#include "Array.cpp"
#endif // !defined _ARRAY_H_
Array.cpp:
#include "BaseArray.h"
#include "Array.h"
#include <stdexcept>
#include <iostream>
template <typename T>
Array <T>::Array (void): BaseArray<T>()
{
std::cout<<"Array def const called"<<std::endl;
}
template <typename T>
Array <T>::Array (const Array & array): BaseArray<T>(array)
{
}
template <typename T>
Array <T>::~Array (void)
{
}
template <typename T>
void Array <T>::resize (size_t new_size)
{
this->data_= this->data_;
if (new_size>this->cur_size_)
{
max_size_ = new_size-this->cur_size_-1;
this->cur_size_=new_size;
for (max_size_; max_size_<=new_size; max_size_++)
this->data_[max_size_]=0;
}
this->cur_size_=new_size;
}
/* Also tried it like this:
template <typename T>
void Array <T>::resize (size_t new_size)
{
BaseArray<T>::data_= BaseArray<T>::data_;
if (new_size>BaseArray<T>::cur_size_)
{
max_size_ = new_size-BaseArray<T>::cur_size_-1;
BaseArray<T>::cur_size_=new_size;
for (max_size_; max_size_<=new_size; max_size_++)
BaseArray<T>::data_[max_size_]=0;
}
BaseArray<T>::cur_size_=new_size;
} */
regarding the first error, you have no max_size() member declared in Array.
regarding the second error, name lookup in templates follows a two stage logic, where non dependent expressions are looked up at definition point, whereas dependent expressions are looked up at instantiation point;
This means that when the compiler sees data_ it thinks it's a variable located somewhere else; at best, it won't find it giving you an error, at worst, it will give you the wrong variable !
In order to solve the problem, you need to make that a dependent expression, the most obvious way being replacing all data_ with this->data_, etc...
regarding your code organization, define your templates into a single header file; if you really want to split member implementations place them in a single file with a sensible file extension ( inl is ok, cpp is not )...
Related
I'm writing a game engine lib, for the sake of science. I've written static libs successfully in the past, although there were no templated functions.
When dealing with templated functions, I use to sepparate their code from the untemplated ones. Templated functions code lie in the header file, while the others in the .cpp/.hpp file.
Below is a snippet of one of it's modules: signals.
// Connection.h
#pragma once
#include <memory>
#include <functional>
namespace mqs
{
using Disconnector = std::function<void(std::uint32_t)>;
class Connection final
{
public:
explicit Connection(std::shared_ptr<mqs::Disconnector> disconnector, std::uint32_t index);
bool connected() const;
void disconnect() const;
private:
std::uint32_t index;
std::weak_ptr<mqs::Disconnector> disconnector;
};
}
// Signal.h
#pragma once
#include <vector>
#include "connection.hpp"
namespace mqs
{
template <typename...>
class Signal;
template <typename R, typename... A>
class Signal<R(A...)> final
{
public:
Signal();
template <typename Lambda>
mqs::Connection connect(Lambda&& lambda) {
slots.push_back(std::forward<Lambda>(lambda));
return mqs::Connection(disconnector, slots.size() - 1U);
}
void operator()(A&&... args) const;
unsigned connections() const;
private:
std::vector<std::function<R(A...)>> slots;
std::shared_ptr<mqs::Disconnector> disconnector;
};
}
// Connection.hpp
#pragma once
#include "connection.h"
namespace mqs
{
Connection::Connection(std::shared_ptr<mqs::Disconnector> disconnector, std::uint32_t index) {
this->index = index;
this->disconnector = disconnector;
}
bool Connection::connected() const {
return !disconnector.expired();
}
void Connection::disconnect() const {
if (const auto& lock = disconnector.lock()) {
lock->operator()(index);
}
}
}
// Signal.hpp
#pragma once
#include "signal.h"
namespace mqs
{
template <typename R, typename... A>
Signal<R(A...)>::Signal() {
disconnector = std::make_shared<mqs::Disconnector>([this](std::uint32_t index) {
slots.erase(slots.begin() + index);
});
}
template <typename R, typename... A>
void Signal<R(A...)>::operator()(A&&... args) const {
for (auto& slot : slots) {
slot(std::forward<A>(args)...);
}
}
template <typename R, typename... A>
unsigned Signal<R(A...)>::connections() const {
return slots.size();
}
}
It compiles and all, however one of the problems I've been dealing with, is that mqs::Signal (signal.hpp) cannot be included in different headers or it will cause a function already has a body. When including signal.h I get unresolved external symbol which makes sense.
I've also tried making inline all the functions defined in their .hpp files above.
Is there any way to achieve this other than using huge header-only approaches?
As you already figured out, you need to make the function templates inline. This is necessary because the templates first need to be instantiated to become compilable functions, and that means the compiler needs source code.
However, if you look at members like Signal<R(A...)>::disconnector;, you'll notice that they are not dependent on R or A.... Hence, you could move them to a non-template base class.
There's a fairly common convention to use the extension .ipp for implementation files that still need to be included, e.g. because they contain template code. These will typically be included by the corresponding .hpp file, just before the #endif of the header guard. Therefore an .ipp file doesn't need its own header guard.
The problem comes from a computer graphics C++ project in which I want to calculate gradient of both scale field and 3D-vector field. We know the gradient of them are different: scale field has 3D-vector gradient while 3D-vector field has a 3x3 matrix gradient. Since all other codes are the same, I am using template to reuse code. But I met with a problem in specializing member functions which has different code for calculating gradient of different data type. The minimized code is as follows:
//======== Main.cpp ========
#include "Render.h"
int main() {}
//======== Render.cpp ========
#include "Render.h"
//======== Render.h ========
#ifndef __RENDER_H__
#define __RENDER_H__
#include "VolumeGrid.h"
#endif
//======== VolumeGrid.h ========
#ifndef __VOLUMEGRID_H__
#define __VOLUMEGRID_H__
#include "Volume.h"
template < typename U >
class _Grid {
public:
const typename GradType<U>::GType grad(const Vector& x) const;
U * values = nullptr;
};
template <>
const Vector _Grid<float>::grad(const Vector& x) const {
return Vector();
}
template <>
const Matrix _Grid<Vector>::grad(const Vector& x) const {
return Matrix();
}
#endif
//======== Volumn.h ========
#ifndef __VOLUME_H__
#define __VOLUME_H__
#include "Vector.h"
#include "Matrix.h"
template <typename U>
struct GradType {
typedef int GType;
};
template<>
struct GradType<float> {
typedef Vector GType;
};
template<>
struct GradType<Vector> {
typedef Matrix GType;
};
template< typename U >
class Volume {
public:
typedef U volumeDataType;
typedef typename GradType<U>::GType volumeGradType;
};
#endif
//======== Vector.h ========
#ifndef __VECTOR_H__
#define __VECTOR_H__
class Vector {
public:
float xyz[3] = { 0,0,0 };
};
#endif
//======== Matrix ========
#ifndef __MATRIX_H__
#define __MATRIX_H__
class Matrix {
public:
float m[3][3];
};
#endif
The error message is:
build/Debug/GNU-Linux/Render.o: In function `Vector::Vector()':
/home/CppApplication_1/VolumeGrid.h:19:
multiple definition of `_Grid<float>::grad(Vector const&) const'
build/Debug/GNU-Linux/Main.o:/home/CppApplication_1/VolumeGrid.h:19:
first defined here
build/Debug/GNU-Linux/Render.o: In function
`_Grid<Vector>::grad(Vector const&) const':
/home/CppApplication_1/VolumeGrid.h:24:
multiple definition of `_Grid<Vector>::grad(Vector const&) const'
build/Debug/GNU-Linux/Main.o:/home/CppApplication_1/VolumeGrid.h:24:
first defined here
As you can see from the code, the two specialized grad function corresponding to different data types are defined only once in VolumeGrid.h, as member function of class Grid<float> and Grid<Vector>, respectively. But the error message says there are multiple definitions of them. The code is compiled with g++ 4.8.4 with C++11 enabled on ubuntu 14.04 64-bit (it compiles well on Visual Studio 2015). The code above is minimized in that removal of any line, e.g., #include "Render.h" in Main.cpp, will make the error disappear. The header inclusion structure and class inheritance hierarchy should not be changed because they are used in actual project. So could you please tell me where is the problem in specializing the grad function and how to fix it? Thanks a lot for your help.
An explicit function template specialization (having no template parameters) is not implicitly inline like actual templates are.
Either move the definitions to a *.cpp file, or mark them inline.
If you move them to a *.cpp file, you should declare them in the header file, like
template <>
const Vector _Grid<float>::grad(const Vector& x) const;
I have three files: Stack.cc, Stack.h and stacktest.cc . I am not sure about which files to include where, and i am getting different errors because of it. Currently, the code from Stack.h is:
#ifndef STACK_H
#define STACK_H
#include<iostream>
using namespace std;
template<typename T>
class Stack
{
public:
Stack();
void push(int);
void pop();
int top();
int size();
bool empty();
private:
class Element
{
public:
int data;
Element *next;
Element(Element *n, T d) : next{n}, data{d} {}
};
Element *first;
int num;
};
#endif
#include"Stack.cc"
the (relevant, i think) code from Stack.cc is:
#include<iostream>
using namespace std;
template<typename T>
Stack<T>::Stack()
{
first=nullptr;
}
template<typename T>
void Stack<T>::push(int)
{
num++;
first = new Element(first, data);
}
Stacktest is currently just a test file attempting to call the default constructor. The errors i currently get are:
In file included from Stack.h:30:0,
from stacktest.cc:2:
Stack.cc: In member function ‘void Stack<T>::push(int)’:
Stack.cc:22:28: error: ‘data’ was not declared in this scope
first = new Element(first, data);
^
Stack.cc: In function ‘int size()’:
Stack.cc:62:11: error: ‘num’ was not declared in this scope
return num;
For some reason it wont let me access private data members. Before i didnt have the include in the .h file and instead included the .h in Stack.cc, and that worked, although wouldnt let me access the stack class from Stacktest.cc(Stacktest.cc just includes Stack.h)
Any help would be greatly appreciated, thanks.
With the below code:
materia.h:
#ifndef MATERIA_H
#define MATERIA_H
class material
{
public:
template <class type>
static material* MakeMaterial(typename type::configtype, long);
template <class type>
void CreateNaturalForm(typename type::configtype, long);
…
};
template <class type>
material* material::MakeMaterial(typename type::configtype Config, long Volume)
{
return type::Spawn(Config, Volume);
}
#endif
materias.h:
#ifndef MATERIAS_H
#define MATERIAS_H
#include "materia.h"
#include "confdef.h"
class solid : public material {
public:
typedef solidmaterial configtype;
…
};
template material* material::MakeMaterial<solid>(solidmaterial, long);
template <class type>
void material::CreateNaturalForm(typename type::configtype Config, long Volume)
{
…
MakeMaterial(Config, Volume); // Error here
…
}
template void material::CreateNaturalForm<solid>(solidmaterial, long);
#endif
confdef.h:
#ifndef CONFDEF_H
#define CONFDEF_H
enum solidmaterial {
WOOD,
…
};
#endif
main.cpp
#include "materia.h"
#include "materias.h"
#include "confdef.h"
int main()
{
material::MakeMaterial(WOOD, 500); // Same error here
}
(Here's an online version of the above code that reproduces the error.)
I get the following compilation error message on the commented line:
No matching function for call to 'MakeMaterial'
What am I doing wrong? Shouldn't the explicit instantiation allow the compiler to see the correct function?
The code compiles if I write MakeMaterial<solid> explicitly, but the whole point here is to deduce type from the Config argument. How can I achieve this?
In the call
MakeMaterial(Config, Volume); // Error here
the compiler is asked to find a match where type::configtype in the function template, is the type of Config.
But nothing tells the compiler what to match type to: this is not an explicit instantiation.
In general there could be hundreds of types that type could be matched to, where type::configtype would be the type of Config. C++ does not support the special case where there is only one such possible type.
How to fix that depends on what you meant to accomplish.
The code below works as long as I keep it all in the "main.cpp" file.
//#include "Travel.h"
//#include "Obj.h"
// "Travel.h"
typedef int travel_t;
class Travel
{
public:
static const travel_t AIR;
static const travel_t WATER;
static const travel_t LAND;
};
// "Travel.cpp"
// #ifndef TRAVEL_H
// #define TRAVEL_H
//
// #include "Travel.h"
const travel_t Travel::AIR = -2;
const travel_t Travel::WATER = -1;
const travel_t Travel::LAND = 0;
// #endif //TRAVEL_H
// "Obj.h"
// #ifndef OBJ_H
// #define OBJ_H
//
//#include "Travel.h"
template<typename T, travel_t travel>
class Obj
{
public:
void foo(){};
};
// #endif //OBJ_H
// "main.cpp"
int main()
{
Obj<int, Travel::AIR> objAirTravel;
objAirTravel.foo();
return 0;
}
However, as soon as I moved code to different headers and implementation files as indicated, it doesn't compile any more. :-( How can I fix that problem? What is the problem/rule behind it? This is the compiler error I get (using gcc):
main.cpp|45|error: 'Travel::AIR' is not a valid template argument for type 'int' because it is a non-constant expression|
main.cpp|45|error: invalid type in declaration before ';' token|
main.cpp|47|error: request for member 'foo' in 'objAirTravel', which is of non-class type 'int'|
In order to use a constant as a template argument, its value must be available in the current translation unit. When you move the definition of Travel::Air to a different source file, its value is no longer available to the compiler in main.
Since it's an integer constant, you can declare the value in the declaration inside the class:
class Travel
{
public:
static const travel_t AIR = -2;
static const travel_t WATER = -1;
static const travel_t LAND = 0;
};
Now the values are available to use as template arguments in any translation unit that includes this class definition.