There are quite a lot similar posts concerning this topic here on SO, however so far I couldn't find a solution to my issue.
I want to add CUDA functionality to an existing C++ project (Window, Visual Studio 2019). Here is what I did so far (based on what I gathered googling around).
Solution Explorer -> Right-Click on Project -> Build Dependencies -> Build Customization -> Tick Checkbox for CUDA 11.2 (.targets, .props)
Created a new 'dummy' CUDA project from Visual Studio, build and started it (VS2019 creates a small project, which adds items of two arrays using CUDA on the GPU). It worked.
Checked and compared Project properties between my existing project and the 'dummy' project and changed the following options:
CUDA C/C++ -> Target Machine Platform -> 64-Bit
Linker -> Additional Dependencies -> added cudart_static.lib
Next I created a class with some test code (which is pretty much completely taken from the 'dummy' project):
SHCalculator.h:
#pragma once
class SHCalculator
{
private:
public:
void DoTestCalculationWithCuda();
};
SHCalculator.cpp:
#include "pch.h"
#include "SHCalculator.h"
#include "SHCalculation.cuh"
void SHCalculator::DoTestCalculationWithCuda()
{
const int arraySize = 5;
const int a[arraySize] = { 1, 2, 3, 4, 5 };
const int b[arraySize] = { 10, 20, 30, 40, 50 };
int c[arraySize] = { 0 };
cudaError_t cudaStatus = AddWithCuda(c, a, b, arraySize);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "addWithCuda failed!");
return;
}
printf("{1,2,3,4,5} + {10,20,30,40,50} = {%d,%d,%d,%d,%d}\n",
c[0], c[1], c[2], c[3], c[4]);
// cudaDeviceReset must be called before exiting in order for profiling and
// tracing tools such as Nsight and Visual Profiler to show complete traces.
cudaStatus = cudaDeviceReset();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceReset failed!");
return;
}
}
SHCalculation.cuh:
cudaError_t AddWithCuda(int* c, const int* a, const int* b, unsigned int size);
//__global__ void addKernel(int* c, const int* a, const int* b);
SHCalculation.cu:
#include "pch.h"
#include "SHCalculation.cuh"
//__global__ void addKernel(int* c, const int* a, const int* b)
//{
// int i = threadIdx.x;
// c[i] = a[i] + b[i];
//}
cudaError_t AddWithCuda(int* c, const int* a, const int* b, unsigned int size)
{
int* dev_a = 0;
int* dev_b = 0;
int* dev_c = 0;
cudaError_t cudaStatus = cudaError_t::cudaErrorAssert;
// left out all the other code (memory allocation, kernel calling) here, since the error
// comes without it as well.
return cudaStatus;
}
Notice, I have commented out or removed a lot of the code, as the error appears regardless. This are the error messages:
LNK2019 unresolved external symbol "enum cudaError __cdecl AddWithCuda(int *,int const *,int const *,unsigned int)" (?AddWithCuda##YA?AW4cudaError##PEAHPEBH1I#Z) referenced in function "public: void __cdecl SHCalculator::DoTestCalculationWithCuda(void)" (?DoTestCalculationWithCuda#SHCalculator##QEAAXXZ)
LNK1120 1 unresolved externals
Additional Info:
CUDA includes are in the precompiled header pch.h:
// cuda
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
What could be the reason for the linker error?
Here is what was missing:
For all CUDA files do:
Right-Click -> Properties
In Configuration Properties -> General -> Item Type choose CUDA C/C++
This is the reason that no *.obj files where created and the linker couldn't link them.
Related
I am following a tutorial here for creating a static library and using it for another project. So I want to create a .lib file and use it for another project.
Static library project:
MyMathLib.h
#define PI 3.1415;
double PowerOf2(double UserNumber);
double PowerOf3(double UserNumber);
double CircleArea(double UserRadius);
double CircleCircum(double UserRadius);
MyMathLib.cpp
#include "stdafx.h"
#include "MyMathLib.h"
double PowerOf2(double UserNumber) { return UserNumber * UserNumber; }
double PowerOf3(double UserNumber) { return UserNumber * UserNumber * UserNumber; }
double CircleArea(double UserRadius) { return UserRadius * UserRadius * PI; }
double CircleCircum(double UserRadius) { return 2 * UserRadius * PI; }
For the second project, I have done the following:
Add the MyMathLib vc project
Common Properties -> References -> Add New Reference
C/C++ -> General -> Additional Include Directories.
This is the C file that tries to call the library:
MyApps1.c
#include <stdio.h>
#include "MyMathLib.h"
int main()
{
double p2 = 10.0;
double radius = 4.0;
printf("The number %.2f to the power of 2 is %.2f. \n", p2, PowerOf2(p2));
printf("A circle with a radius of %.2f, the area is %.2f. \n", radius, CircleArea(radius));
return 0;
}
The error I am getting is:
1>------ Build started: Project: MyApps1, Configuration: Debug Win32 ------
1>MyApps1.obj : error LNK2019: unresolved external symbol _PowerOf2 referenced in function _main
1>MyApps1.obj : error LNK2019: unresolved external symbol _CircleArea referenced in function _main
1>c:\users\bandika\documents\visual studio 2013\Projects\MyApps1\Debug\MyApps1.exe : fatal error LNK1120: 2 unresolved externals
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========
So there is a linking error somewhere. I have tried going to MyApps1 Properties -> Linker -> Input -> Additional Dependencies but I don't think I can add the .lib file for MyMathLib. Any idea what I'm missing?
Its related to linking of your static lib with the second project.
I don't see any problem in adding your generated static library name in "Configuration Properties -> Linker -> Input -> Additional Dependencies".
It should solve the linking problem.
Are you facing any other problem after using this option?
You do not have the second file added to the project in the VS.
I have an MFC application. I would like to trace every dynamic memory allocations (on heap) to be able to find out the source of the memory leaks in that app. The IDE is Visual Studio 2010.
I did the following:
Introduced a preprocessor directive called 'MEMORY_LEAK_FINDER'.
Added a class called 'CMemLeakHunter', you find the exact content of
these files below.
The idea was to overload every new operators (all
3 of them: new, new[] and CObject::new) and use them to trace the
location where the memory was allocated (file, line). At the end of
the execution I wanted to bring the memory leaks' location to the
output using 'CMemoryState' class, so I could finally compare the allocations' trace with the CMemoryState's compare (difference) trace.
The problem is, that the application compiles (in VS 2010 debug mode), but the following linker errors are occurred:
Error 4 error LNK2005: "void * __cdecl operator new[](unsigned int,char const *,int)" (??_U#YAPAXIPBDH#Z) already defined in CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem.obj)
Error 3 error LNK2005: "void * __cdecl operator new(unsigned int,char const *,int)" (??2#YAPAXIPBDH#Z) already defined in CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem.obj)
Error 5 error LNK2005: "public: static void * __stdcall CObject::operator new(unsigned int,char const *,int)" (??2CObject##SGPAXIPBDH#Z) already defined in CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem.obj)
Error 6 error LNK1169: one or more multiply defined symbols found E:\Software\Module1.exe 1
I googled and found out, that ignoring the library Nafxcwd.lib may solve the problem. In my application not, I tried out, but ignoring that library, another 17000 linker error (unresolved externals).
Additional dependencies are: Nafxcwd.lib;Ws2_32.lib;Version.lib
Ignore specific default libraries are: msvcprtd.lib;libcimtd.lib;libcmt.lib
I can't split the software so easily, therefore I ask for help: how could I trace the memory allocations done by my own app if I am using MFC and I need to use the .lib files mentioned above? What could be the solution? Please help me to resolve this matter to be able to trace the memory allocations to find out the possible sources of the leaks. I'm also open-minded to use another MFC built-in routines if they are able to do this. However, I did not find any useful by myself.
The header file CMemLeakHunter.hpp is written as follows:
#ifndef _MEM_LEAK_HUNTER_
#define _MEM_LEAK_HUNTER_
#ifdef MEMORY_LEAK_FINDER
#pragma message("Macro MEMORY_LEAK_FINDER is active, overloading new...")
#include "stdafx.h"
#include <map>
using std::map;
#undef new
void* operator new(size_t size, LPCSTR file, int line);
void* operator new[](size_t size, LPCSTR file, int line);
#define new new(__FILE__, __LINE__)
namespace
{
static const size_t LOG_BUFFER_SIZE = 2;
static const size_t DEFAULT_BUFFER_LINE_SIZE = 512;
}
class CMemLeakHunter
{
public:
static CMemLeakHunter& singleton();
void startMemoryTrace(const char* leakPath, const char* allocPath);
void endMemoryTrace();
void addMemory(void* area, size_t size, LPCSTR file, int line);
void deleteMemory(void* area, LPCSTR file, int line);
private:
void flushAllocLog();
void filterTrace();
map<DWORD, size_t> mMemChunks;
CString sLogBuffer[LOG_BUFFER_SIZE];
size_t nLogBufferLines;
CMemoryState oldMemState;
CMemoryState newMemState;
CMemoryState diffMemState;
CString sMemLeakTracePath;
CString sMemAllocTracePath;
static CMutex s_oObjMutex;
};
#endif // MEMORY_LEAK_FINDER
#endif // _MEM_LEAK_HUNTER_
The source file CMemLeakHunter.cpp is written as follows:
#include "CMemLeakHunter.hpp"
#ifdef MEMORY_LEAK_FINDER
#pragma message("Memory-Leak finder is activated, building functions for the class...")
#undef new
void* operator new(size_t size, LPCSTR file, int line)
{
void* pArea = ::operator new(size);
CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
return pArea;
}
void* operator new[](size_t size, LPCSTR file, int line)
{
void* pArea = ::operator new[](size);
CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
return pArea;
}
void* PASCAL CObject::operator new(size_t size, LPCSTR file, int line)
{
void* pArea = CObject::operator new(size);
CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
return pArea;
}
CMutex CMemLeakHunter::s_oObjMutex;
CMemLeakHunter& CMemLeakHunter::singleton()
{
static CMemLeakHunter theSingleObject;
return theSingleObject;
}
void CMemLeakHunter::startMemoryTrace(const char* leakPath, const char* allocPath)
{
sMemLeakTracePath = leakPath;
sMemAllocTracePath = allocPath;
oldMemState.Checkpoint();
for(size_t i=0; i<LOG_BUFFER_SIZE; i++)
{
sLogBuffer[i] = CString('\0', DEFAULT_BUFFER_LINE_SIZE);
}
}
void CMemLeakHunter::endMemoryTrace()
{
newMemState.Checkpoint();
if(FALSE != diffMemState.Difference(oldMemState, newMemState))
{
CFile oDumpFile;
if(TRUE == oDumpFile.Open(sMemLeakTracePath, CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyWrite))
{
CFile* oldContext = afxDump.m_pFile;
afxDump.m_pFile = &oDumpFile;
diffMemState.DumpStatistics();
oDumpFile.Write("\n", 1);
diffMemState.DumpAllObjectsSince();
oDumpFile.Close();
afxDump.m_pFile = oldContext;
}
else
{
// TODO: log that file cannot be created!!!
}
}
flushAllocLog();
filterTrace();
}
void CMemLeakHunter::addMemory(void* area, size_t size, LPCSTR file, int line)
{
CSingleLock oMemHunterTraceLock(&s_oObjMutex, TRUE);
DWORD nAreaAddr = reinterpret_cast<DWORD>(area);
mMemChunks[nAreaAddr] = size;
if(nLogBufferLines >= LOG_BUFFER_SIZE)
{
flushAllocLog();
}
sLogBuffer[nLogBufferLines++].Format("### Memory allocation: Address 0x%08X, Size: %u, File: '%s', Line: %d\n", nAreaAddr, size, file, line);
}
void CMemLeakHunter::deleteMemory(void* area, LPCSTR file, int line)
{
CSingleLock oMemHunterTraceLock(&s_oObjMutex, TRUE);
DWORD nAreaAddr = reinterpret_cast<DWORD>(area);
mMemChunks.erase(nAreaAddr);
if(nLogBufferLines >= LOG_BUFFER_SIZE)
{
flushAllocLog();
}
sLogBuffer[nLogBufferLines++].Format("!!! Memory release: Address 0x%08X, File: '%s', Line: %d\n", nAreaAddr, file, line);
}
void CMemLeakHunter::flushAllocLog()
{
CStdioFile oAllocFile;
if(FALSE == PathFileExists(sMemAllocTracePath))
{
oAllocFile.Open(sMemAllocTracePath, CFile::modeCreate);
oAllocFile.Close();
}
if(TRUE == oAllocFile.Open(sMemAllocTracePath, CFile::modeReadWrite | CFile::shareDenyWrite))
{
oAllocFile.SeekToEnd();
for(size_t i=0; i<min(nLogBufferLines, LOG_BUFFER_SIZE); i++)
{
oAllocFile.WriteString(sLogBuffer[i]);
}
oAllocFile.Close();
}
else
{
// TODO: log that file cannot be accessed!!!
}
nLogBufferLines = 0;
}
void CMemLeakHunter::filterTrace()
{
CStdioFile oAllocFile;
if(TRUE == oAllocFile.Open(sMemAllocTracePath, CFile::modeRead | CFile::shareDenyWrite))
{
CString sFilterTraceFile;
sFilterTraceFile.Format("filter_%s", sMemAllocTracePath);
CStdioFile oFilterAllocFile;
if(TRUE == oFilterAllocFile.Open(sFilterTraceFile, CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyWrite))
{
for (std::map<DWORD, size_t>::iterator it=mMemChunks.begin(); it!=mMemChunks.end(); it++)
{
CString addrHex;
addrHex.Format("0x%08X", it->first);
CString sLine;
while(FALSE != oAllocFile.ReadString(sLine))
{
if(sLine.Find(addrHex) > -1)
{
CString sLineWithNewline;
sLineWithNewline.Format("%s\n", sLine);
oFilterAllocFile.WriteString(sLineWithNewline);
}
}
}
oFilterAllocFile.Close();
}
else
{
// TODO: log that file cannot be created!!!
}
oAllocFile.Close();
}
else
{
// TODO: log that file cannot be accessed!!!
}
}
#endif // MEMORY_LEAK_FINDER
No need to do this yourself.
In your main.cpp include:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
and at the top of your main function call:
#ifdef _DEBUG
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
For every file where you want to detect leaks place this at the top:
#ifdef _DEBUG
#ifndef DBG_NEW
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define new DBG_NEW
#endif
#endif // _DEBUG
Then any detected leaks are output to the console on application exit.
See here.
I am trying to create a CUDA + C++ project. Basically a .cpp project that calls for some CUDA kernel. So I simply followed the example here, which basically adds two vectors. The kernel does the summation job:
http://blog.norture.com/2012/10/gpu-parallel-programming-in-vs2012-with-nvidia-cuda/
Here is the code,
#include <iostream>
#include "cuda_runtime.h"
#include "cuda.h"
#include "device_launch_parameters.h"
using namespace std;
__global__ void saxpy(int n, float a, float *x, float *y)
{
int i = blockIdx.x*blockDim.x + threadIdx.x;
if (i < n) y[i] = a*x[i] + y[i];
}
int main(void)
{
int N = 1<<20;
float *x, *y, *d_x, *d_y;
x = (float*)malloc(N*sizeof(float));
y = (float*)malloc(N*sizeof(float));
cudaMalloc(&d_x, N*sizeof(float));
cudaMalloc(&d_y, N*sizeof(float));
for (int i = 0; i < N; i++) {
x[i] = 1.0f;
y[i] = 2.0f;
}
cudaMemcpy(d_x, x, N*sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(d_y, y, N*sizeof(float), cudaMemcpyHostToDevice);
// Perform SAXPY on 1M elements
saxpy<<<(N+255)/256, 256>>>(N, 2.0, d_x, d_y);
cudaMemcpy(y, d_y, N*sizeof(float), cudaMemcpyDeviceToHost);
float maxError = 0.0f;
for (int i = 0; i < N; i++)
maxError = max(maxError, abs(y[i]-4.0f));
cout << "Max error: " << maxError;
}
When I built I got this error:
1>------ Rebuild All started: Project: CUDATest001, Configuration: Debug x64 ------
1> CUDATestZeroZeroOne.cpp
1>CUDATestZeroZeroOne.obj : error LNK2001: unresolved external symbol threadIdx
1>CUDATestZeroZeroOne.obj : error LNK2001: unresolved external symbol blockIdx
1>CUDATestZeroZeroOne.obj : error LNK2001: unresolved external symbol blockDim
1>D:\Projects\CUDATest001\x64\Debug\CUDATest001.exe : fatal error LNK1120: 3 unresolved externals
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========
If the line saxpy<<<(N+255)/256, 256>>>(N, 2.0, d_x, d_y); is commented out, then this error appeared:
1>------ Rebuild All started: Project: CUDATest001, Configuration: Debug x64 ------
1> CUDATestZeroZeroOne.cpp
1>CUDATestZeroZeroOne.obj : error LNK2001: unresolved external symbol threadIdx
1>CUDATestZeroZeroOne.obj : error LNK2001: unresolved external symbol blockIdx
1>CUDATestZeroZeroOne.obj : error LNK2001: unresolved external symbol blockDim
1>D:\Projects\CUDATest001\x64\Debug\CUDATest001.exe : fatal error LNK1120: 3 unresolved externals
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========
I am using vs2012 + CUDA 5.5. I started with a empty C++ win32 console project, added a .cpp file which includes all the code above. I am not even sure at this point should it be a .cu or a .cpp file?
Anyone has any idea how to make this work? Thanks.
In the context menu for your project, click Build Customizations. Turn on the CUDA 5.5 target.
In the context menu for your .cpp file, click Rename and rename it to .cu.
In the context menu for your .cu file (that you just renamed), select Properties. Then go to General and make sure Item Type is set to CUDA C/C++.
Rebuild.
When you start a new CUDA project, you can select Templates > NVIDIA > CUDA 5.5 > CUDA 5.5 Runtime to get a project that should compile without any modifications.
I was trying to copy a structure to constant memory in this way:
struct Foo {
int a, b, c;
};
__constant__ Foo cData;
int main() {
Foo hData = {1, 2, 3};
cudaMemcpyToSymbol(cData, &hData, sizeof(Foo));
// ...
}
And this worked fine, in my kernel I could access the constant data directly:
__global__ void kernel() {
printf("Data is: %d %d %d\n", cData.a, cData.b, cData.c); // 1 2 3
}
But then I tried to use a const char * as symbol name, and things stopped working:
cudaMemcpyToSymbol("cData", &hData, sizeof(Foo)); // prints 0 0 0
I thought both versions were similar, but it seems I was wrong.
What is happening?
EDIT:
I'd like to report this same behavior with cudaGetSymbolAddress, which works for me if no const char * is used:
__constant__ int someData[10];
__constant__ int *ptrToData;
int *dataPosition;
cudaGetSymbolAddress((void **)&dataPosition, someData); // Works
// cudaGetSymbolAddress((void **)&dataPosition, "someData"); // Do not work
cudaMemcpyToSymbol(ptrToData, &dataPosition, sizeof(int *));
As of CUDA 5, using a string for symbol names is no longer supported. This is covered in the CUDA 5 release notes here
•The use of a character string to indicate a device symbol, which was possible with certain API functions, is no longer supported. Instead, the symbol should be used directly.
One of the reasons for this has to do with enabling of a true device linker, which is new functionality in CUDA 5.
Because of getting the same error again and again, I want to share this sample code that shows nearly all of the example cases for this problem (so I may refer here later when I make same mistakes again).
//file: main.cu
#include <stdio.h>
#include <stdlib.h>
#include <cuda.h>
__constant__ float constData[256];
__device__ float devData;
__device__ float* devPointer;
int main(int argc, char **argv)
{
cudaFree(0);
float data[256];
cudaError_t err = cudaMemcpyToSymbol(constData, data, sizeof(data));
printf("Err id: %d, str: %s\n", err, cudaGetErrorString(err));
float value = 3.14f;
err = cudaMemcpyToSymbol(devData, &value, sizeof(float));
printf("Err id: %d, str: %s\n", err, cudaGetErrorString(err));
float* ptr;
cudaMalloc(&ptr, 256 * sizeof(float));
err = cudaMemcpyToSymbol(devPointer, &ptr, sizeof(ptr));
printf("Err id: %d, str: %s\n", err, cudaGetErrorString(err));
cudaFree(ptr);
return EXIT_SUCCESS;
}
I was getting "invalid device symbol" and many others which are related to _constant_ _device_ memory usage. This code gives no such errors at runtime.
I am performing a project with ITK for processing medical images. After a lot of work there is no further compilation errors, but in the linking process I am having the following info :
1>------Generation started: proyect: prueba_r01, configuration: Debug Win32 ------
1>Linking…
1>Creating library C:\Documents and Settings\GTTS\Mis documentos\Visual Studio 2008\Projects\prueba_r01\Debug\prueba_r01.lib and object C:\Documents and Settings\GTTS\Mis documentos\Visual Studio 2008\Projects\prueba_r01\Debug\prueba_r01.exp
1>prueba_r01.obj : error LNK2019: extern symbol "public: double (* __thiscall prueba_r01::multiply_matrix_2D(double ()[2],double ()[2],int,int))[2]" (?multiply_matrix_2D#prueba_r01##QAEPAY01NPAY01N0HH#Z) unresolved which is referenced in the function "private: void __thiscall prueba_r01::filtro(void)" (?filtro#prueba_r01##AAEXXZ)
1>C:\Documents and Settings\GTTS\Mis documentos\Visual Studio 2008\Projects\prueba_r01\Debug\prueba_r01.exe : fatal error LNK1120: 1 externos sin resolver
1>prueba_r01 - 2 errors, 0 warnings
========== Generar: 0 corrects, 1 incorrects, 0 actualized, 0 omited ==========
The method multiply_matrix_2D produces the error when is called inside the private slot “filtro()” (translated as filter)
The header of the file is :
#include <QtGui/QMainWindow>
#include "ui_prueba_r01.h"
#include "vicdef.h"
#include "itkImage.h"
#include "math.h"
#include <complex>
#include "fftw3.h"
using namespace std;
#define PI 3.14159265
class prueba_r01 : public QMainWindow
{
Q_OBJECT
public:
typedef double PixelType;
typedef itk::Image < PixelType, 2> ImageType;
ImageType::Pointer imagen;
double** H;
prueba_r01(QWidget *parent = 0, Qt::WFlags flags = 0);
~prueba_r01();
void matrix2D_H(int ancho, int alto, double eta, double sigma);
fftw_complex* multiply_matrix_2D(fftw_complex* out, fftw_complex* H,int a, int b);
private slots:
void openRGB();
void filtro();
private:
Ui::prueba_r01Class ui;
};
#endif // PRUEBA_R01_H
And the main part where the problem is located is in the .cpp file and is displayed here:
fftw_complex* res ;
res = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*a*b);
fftw_complex* H_casted= reinterpret_cast<fftw_complex*> (&H);
res = multiply_matrix_2D(out,H_casted, a, b);
The process of casting a **double pointer to *fftw_complex is done here, because I want to multiply a filter in frequency domain (H(w)) with the result of the fft transform of an image, that’s the reason. Is important to remark that fftw_complex is double[2], the first row for the real part, and the second for the imaginary
And the problematic method is shown below:
fftw_complex* multiply_matrix_2D(fftw_complex* out, fftw_complex* H, int a ,int b){
/* The matrix out[axb] or [n0x(n1/2)+1] is the image after the FFT , and the out_H[axb] is the filter in the frequency domain,
both are multiplied POINT TO POINT, it has to be called twice, one for the imaginary part and another for the normal part
*/
fftw_complex *res;
res = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*a*b);
for (int i0 = 0; i0<a ; i0++){
for (int i1 = 0; i1<b ; i1++){
res[i1+a*i0][0] = out[i1+a*i0][0]*(H[0][0]+H[0][1]); // real part
res[i1+a*i0][1] = out[i1+a*i0][1]*(H[0][0]+H[0][1]); // imaginary part
}
}
return res;
}
Any help will be really nice!! I’m quite lost now…
Thanks! Gracias!
Antonio
Change the function header in the cpp file to:
fftw_complex* prueba_r01::multiply_matrix_2D(fftw_complex* out, fftw_complex* H, int a, int b)
You forgot the class name (prueba_r01::) in the implementation, therefore it doesn't find the function body