I want to create a managed C++ unit test project to test an unmanaged MFC project. I have read msujaws's procedural and followed it. I implemented a test method to test the return string of a function like so:
#include "stdafx.h"
#include "TxStats.h"
#include <cstdlib>
#include <atlstr.h>
#pragma managed
#using <mscorlib.dll>
#using <System.dll>
#using <system.data.dll>
using namespace std;
using namespace System;
using namespace System::Text;
using namespace System::Text::RegularExpressions;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
using namespace Microsoft::VisualStudio::TestTools::UnitTesting;
namespace AUnitTest
{
[TestClass]
public ref class TxStatsTest
{
private:
TestContext^ testContextInstance;
public:
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
property Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ TestContext
{
Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ get()
{
return testContextInstance;
}
System::Void set(Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ value)
{
testContextInstance = value;
}
};
#pragma region Additional test attributes
//
//You can use the following additional attributes as you write your tests:
//
//Use ClassInitialize to run code before running the first test in the class
//[ClassInitialize()]
//static void MyClassInitialize(TestContext^ testContext) {};
//
//Use ClassCleanup to run code after all tests in a class have run
//[ClassCleanup()]
//static void MyClassCleanup() {};
//
//Use TestInitialize to run code before running each test
//[TestInitialize()]
//void MyTestInitialize() {};
//
//Use TestCleanup to run code after each test has run
//[TestCleanup()]
//void MyTestCleanup() {};
//
#pragma endregion
[TestMethod]
void TestGetTxRateStr()
{
/* str to CString
CManagedClass* pCManagedClass = new CManagedClass();
pCManagedClass->ShowMessage(strMessage);
char* szMessage = (char*)Marshal::StringToHGlobalAnsi(strMessage);
CUnmanagedClass cUnmanagedClass; cUnmanagedClass.ShowMessageBox(szMessage);
Marshal::FreeHGlobal((int)szMessage);
*/
CString out = TxStats::GetTxRateStr(1024);
// convert between MFC and .NET String implementations
String ^ myManagedString = Marshal::PtrToStringAnsi((IntPtr) (char *) out.GetBuffer());
String ^ ret = myManagedString ;///gcnew String( );
Regex ^ matStr = gcnew Regex("1024 KB/s");
StringAssert::Matches(ret, matStr);
}
};
}
That tests the code in a DIFFERENT project that looks like this:
#include "stdafx.h"
#include "TxStats.h"
TxStats::TxStats()
{
}
/*
This method returns a data rate string formatted in either Bytes, KBytes, MBytes or GBytes per sec
from an int of the bytes per second.
*/
CString TxStats::GetTxRateStr(__int64 Bps)
{
enum DataUnits dunit;
const __int64 dataSizes[]= { 0x1, // 2 ^ 0
0x400, // 2 ^ 10
0x100000, // 2 ^ 20
0x40000000};// 2 ^ 30
const char *dataStrs[] = { "B/s",
"KB/s",
"MB/s",
"GB/s"};
CString out;
double datarate;
bool finish = false;
for ( dunit = A_KBYTE; dunit <= LARGER_THAN_BIGGEST_UNIT; dunit = DataUnits(dunit+1) )
{
if ( dunit == LARGER_THAN_BIGGEST_UNIT )
{
if (dataSizes[dunit - 1] <= Bps )
{
//Gigabytes / sec
datarate = Bps / ((double) dataSizes[dunit - 1]);
out.Format("%4.2f %s", datarate, dataStrs[dunit - 1]);
finish = true;
break;
}
}
else
{
if (Bps < dataSizes[dunit])
{
//(Kilo, Mega)bytes / sec
datarate = Bps / ((double) dataSizes[dunit - 1]);
out.Format("%4.2f %s", datarate, dataStrs[dunit - 1]);
finish = true;
break;
}
}
}
if (! finish)
{
out.Format("%s", "Unknown!");
}
return out.GetBuffer();
}
void TxStats::BytesToSizeStr(__int64 bytes, CString &out)
{
if (bytes < 0)
{
out = "Err";
}
else if (bytes == 0)
{
out = "0B";
}
else
{
CString size;
CString byteChar = "B";
CString unit;
int val;
if (bytes < 1024)
{
//Bytes
unit = "";
val = (int)bytes;
}
else if ( (bytes >> 10) < 1024 )
{
//Kilobytes
unit = "K";
__int64 div = 1 << 10;
val = (int) (bytes / ((double) div ));
}
else if ( (bytes >> 20) < 1024 )
{
//Megabytes
unit = "M";
__int64 div = 1 << 20;
val = (int) (bytes / ((double) div ));
}
else
{
//Else assume gigabytes
unit = "G";
__int64 div = 1 << 30;
val = (int) (bytes / ((double) div ));
}
unit = unit + byteChar;
const char * unitCharBuf = unit.GetBuffer();
size.Format("%d%s", ((int) val), unitCharBuf);
out = size.GetBuffer();
}
}
However, when I compile this code I get the following error:
2>TxStatsTest.obj : error LNK2028: unresolved token (0A0005D4) "public: static class ATL::CStringT<char,class StrTraitMFC_DLL<char,class ATL::ChTraitsCRT<char> > > __cdecl TxStats::GetTxRateStr(__int64)" (?GetTxRateStr#TxStats##$$FSA?AV?$CStringT#DV?$StrTraitMFC_DLL#DV?$ChTraitsCRT#D#ATL#####ATL##_J#Z) referenced in function "public: void __clrcall AUnitTest::TxStatsTest::TestGetTxRateStr(void)" (?TestGetTxRateStr#TxStatsTest#AUnitTest##$$FQ$AAMXXZ)
2>TxStatsTest.obj : error LNK2019: unresolved external symbol "public: static class ATL::CStringT<char,class StrTraitMFC_DLL<char,class ATL::ChTraitsCRT<char> > > __cdecl TxStats::GetTxRateStr(__int64)" (?GetTxRateStr#TxStats##$$FSA?AV?$CStringT#DV?$StrTraitMFC_DLL#DV?$ChTraitsCRT#D#ATL#####ATL##_J#Z) referenced in function "public: void __clrcall AUnitTest::TxStatsTest::TestGetTxRateStr(void)" (?TestGetTxRateStr#TxStatsTest#AUnitTest##$$FQ$AAMXXZ)
2>\trunk\<proj>\Debug\AUnitTest.dll : fatal error LNK1120: 2 unresolved externals
2>Caching metadata information for c:\program files\microsoft visual studio 9.0\common7\ide\publicassemblies\microsoft.visualstudio.qualitytools.unittestframework.dll...
2>Build log was saved at "file://trunk\<proj>\AUnitTest\Debug\BuildLog.htm"
2>AUnitTest - 3 error(s), 0 warning(s)
========== Rebuild All: 1 succeeded, 1 failed, 0 skipped ==========
Can anyone suggest why the Unit test project might not be linking against the obj files of the main project? (I have already specified the main project as a dependency of the unit test project)
You can add
#pragma comment(lib, "TxStats.lib")
to your unit test project's stdafx.cpp to link against the other library.
You need to add the *.obj files of the project you want to test to the linker inputs of the unit test project
Related
I encountered a strange behavior in my C++ program that I don't understand and I don't know how to search for more information. So I ask for advice here hoping someone might know.
I have a class Interface that has a 2 dimensional vector that I initialize in the header :
class Interface {
public:
// code...
const unsigned short int SIZE_X_ = 64;
const unsigned short int SIZE_Y_ = 32;
std::vector<std::vector<bool>> screen_memory_ =
std::vector<std::vector<bool>>(SIZE_X_, std::vector<bool>(SIZE_Y_, false));
// code...
};
Here I expect that I have a SIZE_X_ x SIZE_Y_ vector filled with false booleans.
Later in my program I loop at a fixed rate like so :
void Emulator::loop() {
const milliseconds intervalPeriodMillis{static_cast<int>((1. / FREQ) * 1000)};
//Initialize the chrono timepoint & duration objects we'll be //using over & over inside our sleep loop
system_clock::time_point currentStartTime{system_clock::now()};
system_clock::time_point nextStartTime{currentStartTime};
while (!stop) {
currentStartTime = system_clock::now();
nextStartTime = currentStartTime + intervalPeriodMillis;
// ---- Stuff happens here ----
registers_->trigger_timers();
interface_->toogle_buzzer();
interface_->poll_events();
interface_->get_keys();
romParser_->step();
romParser_->decode();
// ---- ------------------ ----
stop = stop || interface_->requests_close();
std::this_thread::sleep_until(nextStartTime);
}
}
But then during the execution I get a segmentation fault
[1] 7585 segmentation fault (core dumped) ./CHIP8 coin.ch8
I checked with the debugger and some part of the screen_memory_ cannot be accessed anymore. And it seems to happen at random time.
But when I put the initialization of the vector in the constructor body like so :
Interface::Interface(const std::shared_ptr<reg::RegisterManager> & registers, bool hidden)
: registers_(registers) {
// code ...
screen_memory_ =
std::vector<std::vector<bool>>(SIZE_X_, std::vector<bool>(SIZE_Y_, false));
// code ...
}
The segmentation fault doesn't happen anymore. So the solution is just to initialize the vector in the constructor body.
But why ? what is happening there ?
I don't understand what I did wrong, I'm sure someone knows.
Thanks for your help !
[Edit] I found the source of the bug (Or at least what to change so it doesnt give me a segfault anymore).
In my class Interface I use the SDL and SDL_audio libraries to create the display and the buzzer sound. Have a special look where I set the callback want_.callback, the callback Interface::forward_audio_callback and Interface::audio_callback. Here's the code :
// (c) 2021 Maxandre Ogeret
// Licensed under MIT License
#include "Interface.h"
Interface::Interface(const std::shared_ptr<reg::RegisterManager> & registers, bool hidden)
: registers_(registers) {
if (SDL_Init(SDL_INIT_AUDIO != 0) || SDL_Init(SDL_INIT_VIDEO) != 0) {
throw std::runtime_error("Unable to initialize rendering engine.");
}
want_.freq = SAMPLE_RATE;
want_.format = AUDIO_S16SYS;
want_.channels = 1;
want_.samples = 2048;
want_.callback = Interface::forward_audio_callback;
want_.userdata = &sound_userdata_;
if (SDL_OpenAudio(&want_, &have_) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to open audio: %s", SDL_GetError());
}
if (want_.format != have_.format) {
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to get the desired AudioSpec");
}
window = SDL_CreateWindow("CHIP8", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SIZE_X_ * SIZE_MULTIPLIER_, SIZE_Y_ * SIZE_MULTIPLIER_,
hidden ? SDL_WINDOW_HIDDEN : 0);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
bpp_ = SDL_GetWindowSurface(window)->format->BytesPerPixel;
SDL_Delay(1000);
// screen_memory_ = std::vector<std::vector<bool>>(SIZE_X_, std::vector<bool>(SIZE_Y_, false));
}
Interface::~Interface() {
SDL_CloseAudio();
SDL_DestroyWindow(window);
SDL_Quit();
}
// code ...
void Interface::audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) {
audio_buffer_ = reinterpret_cast<Sint16 *>(raw_buffer);
sample_length_ = bytes / 2;
int & sample_nr(*(int *) user_data);
for (int i = 0; i < sample_length_; i++, sample_nr++) {
double time = (double) sample_nr / (double) SAMPLE_RATE;
audio_buffer_[i] = static_cast<Sint16>(
AMPLITUDE * (2 * (2 * floor(220.0f * time) - floor(2 * 220.0f * time)) + 1));
}
}
void Interface::forward_audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) {
static_cast<Interface *>(user_data)->audio_callback(user_data, raw_buffer, bytes);
}
}
In the function Interface::audio_callback, replacing the class variable assignation :
sample_length_ = bytes / 2;
By an int creation and assignation :
int sample_length = bytes / 2;
which gives :
void Interface::audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) {
audio_buffer_ = reinterpret_cast<Sint16 *>(raw_buffer);
int sample_length = bytes / 2;
int &sample_nr(*(int*)user_data);
for(int i = 0; i < sample_length; i++, sample_nr++)
{
double time = (double)sample_nr / (double)SAMPLE_RATE;
audio_buffer_[i] = (Sint16)(AMPLITUDE * sin(2.0f * M_PI * 441.0f * time)); // render 441 HZ sine wave
}
}
The class variable sample_length_ is defined and initialized as private in the header like so :
int sample_length_ = 0;
So I had an idea and I created the variable sample_length_ as public and it works ! So the problem was definitely a scope problem of the class variable sample_length_. But it doesn't explain why the segfault disappeared when I moved the init of some other variable in the class constructor... Did I hit some undefined behavior with my callback ?
Thanks for reading me !
I need to create a custom blueprint node. I am using the blueprint function library.
The node will look like this:
Input:
int timedelayforeachloop
int numberofloops
output:
exc loop
exc completed
loop1.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "loop1.generated.h"
/**
*
*/
UENUM(BlueprintType)
enum class EMultiBranchEnum1 : uint8
{
BranchA,
BranchB
};
UCLASS()
class MYPROJECT2_API Uloop1 : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
UFUNCTION(BlueprintCallable, meta = (DisplayName = "loop", CompactNodeTitle = "2as2", ExpandEnumAsExecs = "Branches"), Category = "1")
//UFUNCTION(BlueprintCallable, Meta = (DisplayName = "Multi Branch1", ExpandEnumAsExecs = "Branches"), Category = 1)
static void multiBranch(EMultiBranchEnum1& Branches, int loopqty);
//EMultiBranchEnum1::BranchB;
};
loop1.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "loop1.h"
void Uloop1::multiBranch(EMultiBranchEnum1& Branches, int loopqty)
{
int currloop1 = 0;
int temp = 2;
int i;
for (i = 0; i < 10; i++){
currloop1 = currloop1 + 1;
Branches = EMultiBranchEnum1::BranchA;
}
if (temp > currloop1) {
Branches = EMultiBranchEnum1::BranchB;
}
if(temp == 0) {
Branches = EMultiBranchEnum1::BranchB;
}
}
-- THE PROBLEM --
The for loop only runs the once (evident by the print node i have on branchA(It only prints a single time))
-- What should happen with the code below --
the loop should run the 10 times (my print node should print 10 times)
Instead of using UBlueprintFunctionLibrary, you should use UBlueprintAsyncActionBase. It will allow you to store state in the node and call things connected to the execution pins asynchronously.
DelayLoop.h file:
#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "DelayLoop.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDelayOutputPin);
/**
*
*/
UCLASS()
class TEST_API UDelayLoop : public UBlueprintAsyncActionBase
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(BlueprintAssignable)
FDelayOutputPin Loop;
UPROPERTY(BlueprintAssignable)
FDelayOutputPin Complete;
UFUNCTION(BlueprintCallable,
meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"),
Category = "Flow Control")
static UDelayLoop* DelayLoop(const UObject* WorldContextObject,
const float DelayInSeconds, const int Iterations);
virtual void Activate() override;
private:
const UObject* WorldContextObject;
float MyDelay;
int MyIterations;
bool Active;
UFUNCTION()
void ExecuteLoop();
UFUNCTION()
void ExecuteComplete();
};
DelayLoop.cpp file:
#include "DelayLoop.h"
#include "Engine/World.h"
#include "TimerManager.h"
UDelayLoop::UDelayLoop(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer), WorldContextObject(nullptr), MyDelay(0.0f),
MyIterations(0), Active(false)
{
}
UDelayLoop* UDelayLoop::DelayLoop(const UObject* WorldContextObject,
const float DelayInSeconds, const int Iterations)
{
UDelayLoop* Node = NewObject<UDelayLoop>();
Node->WorldContextObject = WorldContextObject;
Node->MyDelay = DelayInSeconds;
Node->MyIterations = Iterations;
return Node;
}
void UDelayLoop::Activate()
{
if (nullptr == WorldContextObject)
{
FFrame::KismetExecutionMessage(TEXT("Invalid WorldContextObject."),
ELogVerbosity::Error);
return;
}
if (Active)
{
FFrame::KismetExecutionMessage(TEXT("DelayLoop is already running."),
ELogVerbosity::Warning);
}
if (MyDelay <= 0.0f)
{
FFrame::KismetExecutionMessage(
TEXT("DelayLoop delay can't be less or equal to 0."),
ELogVerbosity::Warning);
}
if (MyIterations <= 0)
{
FFrame::KismetExecutionMessage(
TEXT("DelayLoop iterations can't be less or equal to 0."),
ELogVerbosity::Warning);
}
Active = true;
for (int i = 0; i <= MyIterations; i++)
{
FTimerHandle IterationTimer;
WorldContextObject->GetWorld()->GetTimerManager().SetTimer(
IterationTimer, this, &UDelayLoop::ExecuteLoop, MyDelay * i);
}
FTimerHandle CompleteTimer;
WorldContextObject->GetWorld()->GetTimerManager().SetTimer(
CompleteTimer, this, &UDelayLoop::ExecuteComplete,
MyDelay * (MyIterations+1));
// If the Complete pin should happen at the same time as the last iteration
// use `MyDelay * MyIterations` here instead
}
void UDelayLoop::ExecuteLoop()
{
Loop.Broadcast();
}
void UDelayLoop::ExecuteComplete()
{
Complete.Broadcast();
Active = false;
}
This will get you a blueprint that looks like this:
Note: This code is heavily based on This Creating Asynchronous Blueprint Nodes guide by Daniel ~b617 Janowski, now hosted in the legacy wiki here
I am a noobie with C++. My first learning project uses GoogleTest and GoolgleMock, but, of course, I am new to those also. I installed googletestmock.v.141 v101 via NuGet. My main app, AstronomyCalculations, builds and runs without a problem. My test app, GMock, throws three LNK2019 errors when I try to build it.
Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol "public: __cdecl easter::easter(void)" (??0easter##QEAA#XZ) referenced in function "private: virtual void __cdecl GET_THE_DATE_OF_EASTER_ShouldReturnDateOfEaster_Test::TestBody(void)" (?TestBody#GET_THE_DATE_OF_EASTER_ShouldReturnDateOfEaster_Test##EEAAXXZ) GMock D:\Dev\Projects\AstronomyCalculations\GMock\GMock.obj 1
Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol "public: __cdecl easter::~easter(void)" (??1easter##QEAA#XZ) referenced in function "private: virtual void __cdecl GET_THE_DATE_OF_EASTER_ShouldReturnDateOfEaster_Test::TestBody(void)" (?TestBody#GET_THE_DATE_OF_EASTER_ShouldReturnDateOfEaster_Test##EEAAXXZ) GMock D:\Dev\Projects\AstronomyCalculations\GMock\GMock.obj 1
Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol "public: struct tm __cdecl easter::get_easter_date(int)const " (?get_easter_date#easter##QEBA?AUtm##H#Z) referenced in function "private: virtual void __cdecl GET_THE_DATE_OF_EASTER_ShouldReturnDateOfEaster_Test::TestBody(void)" (?TestBody#GET_THE_DATE_OF_EASTER_ShouldReturnDateOfEaster_Test##EEAAXXZ) GMock D:\Dev\Projects\AstronomyCalculations\GMock\GMock.obj 1
// AstronomyCalculations.cpp
int main() {
return 0;
}
// Easter.h
#pragma once
#include <ctime>
#include <string>
class easter
{
public:
easter();
~easter();
tm get_easter_date(int easter_year) const;
};
// Easter.cpp
#include "Easter.h"
easter::easter()
{
}
easter::~easter()
= default;
tm easter::get_easter_date(const int easter_year) const
{
const auto a = easter_year % 19;
const auto b = easter_year / 100;
const auto c = easter_year % 100;
const auto d = b / 4;
const auto e = b % 4;
const auto f = (b + 8) / 25;
const auto g = (b - f + 1) / 3;
const auto h = ((19 * a) + b - d - g + 15) % 30;
const auto i = c / 4;
const auto k = c % 4;
const auto l = (32 + (2 * e) + (2 * i) - h - k) % 7;
const auto m = (a + (11 * h) + (22 * l)) / 451;
const auto easter_month = (h + l - (7 * m) + 114) / 31;
const auto easter_day = ((h + l - (7 * m) + 114) % 31) + 1;
auto date_string = std::to_string(easter_year) +
"-" +
std::to_string(easter_month) +
"-" +
std::to_string(easter_day) +
" 00:00:00";
char date[20]; //a 1 char space for null is also required
strcpy_s(date, date_string.c_str());
tm ltm{};
char seps[] = " -:";
char *next_token = nullptr;
auto token = strtok_s(date, seps, &next_token);
ltm.tm_year = strtol(token, nullptr, 10);
token = strtok_s(nullptr, seps, &next_token);
ltm.tm_mon = strtol(token, nullptr, 10);
token = strtok_s(nullptr, seps, &next_token);
ltm.tm_mday = strtol(token, nullptr, 10);
token = strtok_s(nullptr, seps, &next_token);
ltm.tm_hour = strtol(token, nullptr, 10);
token = strtok_s(nullptr, seps, &next_token);
ltm.tm_min = strtol(token, nullptr, 10);
token = strtok_s(nullptr, seps, &next_token);
ltm.tm_sec = strtol(token, nullptr, 10);
ltm.tm_wday = 0;
return ltm;
}
// GMock.cpp
#include "stdafx.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "Easter.h"
int main(int argc, char** argv)
{
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
TEST(GET_THE_DATE_OF_EASTER, ShouldReturnDateOfEaster)
{
easter estr;
const auto result = estr.get_easter_date(2000);
ASSERT_EQ(result.tm_year, 2000);
}
The linker seems unable to find the functions easter::easter(void), easter::~easter(void), and easter::get_easter_date(int)const
You are obviously including easter.h (and have it in your search path, or the compiler would have complained) but are you actually compiling easter.cpp or a mock object of it? (As far as I can tell from your sourcecode you are not actually mocking the class either). The header file is enough to satisfy the compiler, but when the linker tries to put the application together and realizes it is missing the object file, it balks - throwing LNK2019.
I can only strongly reccommend you look deeper into the documentation available for gtest & gmock.
In addition, you may also reconsider your naming schemes - Class types usually begin with an uppercase (i.e. Easter, not easter) to better tell them from variable names (which begin with lowercase). Also don't call your variables a, b, c, d - const auto a and its ilk are horrible to read, debug, and you will curse yourself I you ever find yourself in the postion of having to revisit the code after some weeks. Even if this were only some practice project, please also practice using good coding standards & naming conventions, so you (automatically) use them on bigger projects.
I finally solved this when I discovered that GoogleTest can be added in Visual Studio 2017 as a new project. Of course, I had to remove my original test project first and then add the new test project. It all worked out in the end, but, of course, I have no idea of why the link errors showed up in my original testing project; other than CharonX's insights.
I'm using C++ and Visual Studio Professional 2013 to write a wrapper for the Windows API (to make it easier to read). When I try to compile the solution, I get two linker errors related to the Intersect function in the wRectangle class. I commented out the function to see what the cause of the problem was, and the solution compiled and ran fine. From this, I determined that it was because the return type was an object of the class name (i.e. The Intersect function in the wRectangle class returns a wRectangle object). Moving the implementation of the Intersect function to its prototype in Rectangle.h fixed the issue, but I would prefer to keep the implementation in Rectangle.cpp. Any way to solve this?
Relevant files, most of the comments have been removed since they serve no relevance:
Rectangle.h:
#include <Windows.h>
#include "Typedefs.h"
#include "Window.h"
namespace windows_API_Wrapper
{
#ifndef RECTANGLE_H
#define RECTANGLE_H
class wRectangle
{
public:
wRectangle();
wRectangle(uInt32 xMin, uInt32 xMax, uInt32 yMin, uInt32 yMax);
void operator=(wRectangle &assignment);
void operator=(Window assignment);
boolean operator!=(wRectangle nonComparable);
wRectangle Intersect(wRectangle source1, wRectangle source2);
void Normalize();
private:
rectangle rect; //A wRectangle structure (typedef of a RECT strucure), for internal use only
};
#endif //RECTANGLE_H
}
Rectangle.cpp:
#include "Rectangle.h"
using namespace windows_API_Wrapper;
wRectangle::wRectangle()
{
this->rect.left = 0;
this->rect.top = 0;
this->rect.right = 0;
this->rect.bottom = 0;
}
void wRectangle::operator=(wRectangle &assignment)
{
this->rect.left = assignment.rect.left;
this->rect.top = assignment.rect.top;
this->rect.right = assignment.rect.right;
this->rect.bottom = assignment.rect.bottom;
}
void wRectangle::operator=(Window assignment)
{
if (GetClientRect(assignment.Get(), &this->rect) == 0)
MessageBox(null, "Failed to assign window dimensions to wRectangle!", "ERROR", MB_ICONEXCLAMATION);
}
boolean wRectangle::operator==(wRectangle comparable)
{
return EqualRect(&this->rect, &comparable.rect);
}
boolean wRectangle::operator!=(wRectangle nonComparable)
{
return !EqualRect(&this->rect, &nonComparable.rect);
}
wRectangle wRectangle::Intersect(wRectangle source1, wRectangle source2)
{
wRectangle destination;
IntersectRect(&destination.rect, &source1.rect, &source2.rect);
return destination;
}
void wRectangle::Normalize()
{
if (this->rect.top > this->rect.bottom)
{
uInt32 temp = this->rect.top;
this->rect.top = this->rect.bottom;
this->rect.bottom = temp;
}
if (this->rect.left > this->rect.right)
{
uInt32 temp = this->rect.left;
this->rect.left = this->rect.right;
this->rect.right = temp;
}
}
Error 1:
error LNK2019: unresolved external symbol "public: __thiscall
windows_API_Wrapper::wRectangle::wRectangle(class windows_API_Wrapper::wRectangle const &)" (??
0wRectangle#windows_API_Wrapper##QAE#ABV01##Z) referenced in function "public: class
windows_API_Wrapper::wRectangle __thiscall windows_API_Wrapper::wRectangle::Intersect(class
windows_API_Wrapper::wRectangle,class windows_API_Wrapper::wRectangle)" (?
Intersect#wRectangle#windows_API_Wrapper##QAE?AV12#V12#0#Z)
File: G:\Visual Studio Projects\Windows API Wrapper\Windows API Wrapper\Rectangle.obj
Project: Windows API Wrapper
Error 2:
error LNK1120: 1 unresolved externals
File: G:\Visual Studio Projects\Windows API Wrapper\Debug\Windows API Wrapper.exe
Project: Windows API Wrapper
This is because you have not defined
wRectangle(uInt32 xMin, uInt32 xMax, uInt32 yMin, uInt32 yMax);
I have been trying to create a DLL out of the HEVC reference decoder and integrate into my application and I'm stuck with a linking error which I'm not able to solve
I have tried adding project dependencies/references. I have attached the code snippet below. I keep getting the following linking error.
HEVCDecoderDXVA.lib(HEVCRefDecImp.obj) : error LNK2019: unresolved external symbol "public: __thiscall TAppDecTop::TAppDecTop(void)" (??0TAppDecTop##QAE#XZ) referenced in function "public: __thiscall CHEVC::CHEVC(void)" (??0CHEVC##QAE#XZ)
TLibDecoder.lib(TDecGop.obj) : error LNK2001: unresolved external symbol "bool g_md5_mismatch" (?g_md5_mismatch##3_NA)
C:\HEVCDecoder3\build\Debug\HEVCDecoderDLL.dll : fatal error LNK1120: 2 unresolved externals
What I'm trying to do is to have decoder instance as a member variable in my CHEVC class
TAppDecTop m_RefDecoder;
The above line is the problem causing line , The unresolved external symbol "bool g_md5_mismatch" error is also due to this line,
In LibraryAPI.h
#ifndef _LIBRARY_API_H_
#define _LIBRARY_API_H_
namespace NSF
{
class CAPI
{
public:
virtual ~CAPI() { }
};
}
#endif // _LIBRARY_API_H_
In AppDecInf.h
#ifndef REF_DEC_INF_H
#define REF_DEC_INF_H
#include "LibraryAPI.h"
namespace NAppDecoderAPI
{
class AppDecInf : public NSF::CAPI
{
public:
virtual ~AppDecInf()
{
}
virtual int InitRefDecoder(char *fileName)=0;
virtual int TerminateRefDecoder()=0;
virtual int DecodeFrame()=0;
};
}
In HEVCRefDecImp.h
#ifndef HEVC_REF_DEC_INF_H
#define HEVC_REF_DEC_INF_H
#include "AppDecInf.h"
#include "TAppDecTop.h"
class CHEVC : public NAppDecoderAPI::AppDecInf
{
public:
CHEVC();
virtual ~CHEVC();
int InitRefDecoder(char *fileName);
int TerminateRefDecoder();
int DecodeFrame();
private:
bool temp;
TAppDecTop m_RefDecoder;
};
#endif //HEVC_REF_DEC_INF_H
In HEVCRefDecImp.cpp
#include"HEVCRefDecImp.h"
CHEVC::CHEVC()
{
temp = false;
}
CHEVC::~CHEVC()
{
}
int CHEVC::InitRefDecoder(char* fileName)
{
m_RefDecoder.create();
return(0);
}
int CHEVC::DecodeFrame()
{
m_RefDecoder.decode();
return(0);
}
int CHEVC::TerminateRefDecoder()
{
m_RefDecoder.destroy();
return(0);
}
In TAppDecTop.h
#ifndef __TAPPDECTOP__
#define __TAPPDECTOP__
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "TLibVideoIO/TVideoIOYuv.h"
#include "TLibCommon/TComList.h"
#include "TLibCommon/TComPicYuv.h"
#include "TLibDecoder/TDecTop.h"
#include "TAppDecCfg.h"
//! \ingroup TAppDecoder
//! \{
// ====================================================================================================================
// Class definition
// ====================================================================================================================
/// decoder application class
class TAppDecTop : public TAppDecCfg
{
private:
// class interface
TDecTop m_cTDecTop; ///< decoder class
TVideoIOYuv m_cTVideoIOYuvReconFile; ///< reconstruction YUV class
// for output control
Bool m_abDecFlag[ MAX_GOP ]; ///< decoded flag in one GOP
Int m_iPOCLastDisplay; ///< last POC in display order
public:
TAppDecTop();
virtual ~TAppDecTop() {}
Void create (); ///< create internal members
Void destroy (); ///< destroy internal members
Void decode (); ///< main decoding function
protected:
Void xCreateDecLib (); ///< create internal classes
Void xDestroyDecLib (); ///< destroy internal classes
Void xInitDecLib (); ///< initialize decoder class
Void xWriteOutput ( TComList<TComPic*>* pcListPic , UInt tId); ///< write YUV to file
Void xFlushOutput ( TComList<TComPic*>* pcListPic ); ///< flush all remaining decoded pictures to file
};
//! \}
#endif
In TAppDecTop.cpp
#include <list>
#include <vector>
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include "TAppDecTop.h"
#include "TLibDecoder/AnnexBread.h"
#include "TLibDecoder/NALread.h"
//! \ingroup TAppDecoder
//! \{
// ====================================================================================================================
// Constructor / destructor / initialization / destroy
// ====================================================================================================================
TAppDecTop::TAppDecTop()
{
::memset (m_abDecFlag, 0, sizeof (m_abDecFlag));
m_iPOCLastDisplay = -MAX_INT;
}
Void TAppDecTop::create()
{
}
Void TAppDecTop::destroy()
{
}
// ====================================================================================================================
// Public member functions
// ====================================================================================================================
/**
- create internal class
- initialize internal class
- until the end of the bitstream, call decoding function in TDecTop class
- delete allocated buffers
- destroy internal class
.
*/
Void TAppDecTop::decode()
{
UInt uiPOC;
TComList<TComPic*>* pcListPic = NULL;
ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary);
if (!bitstreamFile)
{
fprintf(stderr, "\nfailed to open bitstream file `%s' for reading\n", m_pchBitstreamFile);
exit(EXIT_FAILURE);
}
InputByteStream bytestream(bitstreamFile);
// create & initialize internal classes
xCreateDecLib();
xInitDecLib ();
m_iPOCLastDisplay += m_iSkipFrame; // set the last displayed POC correctly for skip forward.
// main decoder loop
bool recon_opened = false; // reconstruction file not yet opened. (must be performed after SPS is seen)
while (!!bitstreamFile)
{
/* location serves to work around a design fault in the decoder, whereby
* the process of reading a new slice that is the first slice of a new frame
* requires the TDecTop::decode() method to be called again with the same
* nal unit. */
streampos location = bitstreamFile.tellg();
AnnexBStats stats = AnnexBStats();
bool bPreviousPictureDecoded = false;
vector<uint8_t> nalUnit;
InputNALUnit nalu;
byteStreamNALUnit(bytestream, nalUnit, stats);
// call actual decoding function
bool bNewPicture = false;
if (nalUnit.empty())
{
/* this can happen if the following occur:
* - empty input file
* - two back-to-back start_code_prefixes
* - start_code_prefix immediately followed by EOF
*/
fprintf(stderr, "Warning: Attempt to decode an empty NAL unit\n");
}
else
{
read(nalu, nalUnit);
if(m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer)
{
if(bPreviousPictureDecoded)
{
bNewPicture = true;
bPreviousPictureDecoded = false;
}
else
{
bNewPicture = false;
}
}
else
{
bNewPicture = m_cTDecTop.decode(nalu, m_iSkipFrame, m_iPOCLastDisplay);
if (bNewPicture)
{
bitstreamFile.clear();
/* location points to the current nalunit payload[1] due to the
* need for the annexB parser to read three extra bytes.
* [1] except for the first NAL unit in the file
* (but bNewPicture doesn't happen then) */
bitstreamFile.seekg(location-streamoff(3));
bytestream.reset();
}
bPreviousPictureDecoded = true;
}
}
if (bNewPicture || !bitstreamFile)
{
m_cTDecTop.executeDeblockAndAlf(uiPOC, pcListPic, m_iSkipFrame, m_iPOCLastDisplay);
}
if( pcListPic )
{
if ( m_pchReconFile && !recon_opened )
{
if ( m_outputBitDepth == 0 )
{
m_outputBitDepth = g_uiBitDepth + g_uiBitIncrement;
}
m_cTVideoIOYuvReconFile.open( m_pchReconFile, true, m_outputBitDepth, g_uiBitDepth + g_uiBitIncrement ); // write mode
recon_opened = true;
}
if (bNewPicture && (nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR))
{
xFlushOutput( pcListPic );
}
// write reconstruction to file
if(bNewPicture)
{
xWriteOutput( pcListPic, nalu.m_temporalId );
}
}
}
xFlushOutput( pcListPic );
// delete buffers
m_cTDecTop.deletePicBuffer();
// destroy internal classes
xDestroyDecLib();
}
// ====================================================================================================================
// Protected member functions
// ====================================================================================================================
Void TAppDecTop::xCreateDecLib()
{
// create decoder class
m_cTDecTop.create();
}
Void TAppDecTop::xDestroyDecLib()
{
if ( m_pchReconFile )
{
m_cTVideoIOYuvReconFile. close();
}
// destroy decoder class
m_cTDecTop.destroy();
}
Void TAppDecTop::xInitDecLib()
{
// initialize decoder class
m_cTDecTop.init();
m_cTDecTop.setPictureDigestEnabled(m_pictureDigestEnabled);
}
/** \param pcListPic list of pictures to be written to file
\todo DYN_REF_FREE should be revised
*/
Void TAppDecTop::xWriteOutput( TComList<TComPic*>* pcListPic, UInt tId )
{
TComList<TComPic*>::iterator iterPic = pcListPic->begin();
Int not_displayed = 0;
while (iterPic != pcListPic->end())
{
TComPic* pcPic = *(iterPic);
if(pcPic->getOutputMark() && pcPic->getPOC() > m_iPOCLastDisplay)
{
not_displayed++;
}
iterPic++;
}
iterPic = pcListPic->begin();
while (iterPic != pcListPic->end())
{
TComPic* pcPic = *(iterPic);
TComSPS *sps = pcPic->getSlice(0)->getSPS();
if ( pcPic->getOutputMark() && (not_displayed > pcPic->getSlice(0)->getSPS()->getNumReorderPics(tId) && pcPic->getPOC() > m_iPOCLastDisplay))
{
// write to file
not_displayed--;
if ( m_pchReconFile )
{
m_cTVideoIOYuvReconFile.write( pcPic->getPicYuvRec(), sps->getPicCropLeftOffset(), sps->getPicCropRightOffset(), sps->getPicCropTopOffset(), sps->getPicCropBottomOffset() );
}
// update POC of display order
m_iPOCLastDisplay = pcPic->getPOC();
// erase non-referenced picture in the reference picture list after display
if ( !pcPic->getSlice(0)->isReferenced() && pcPic->getReconMark() == true )
{
#if !DYN_REF_FREE
pcPic->setReconMark(false);
// mark it should be extended later
pcPic->getPicYuvRec()->setBorderExtension( false );
#else
pcPic->destroy();
pcListPic->erase( iterPic );
iterPic = pcListPic->begin(); // to the beginning, non-efficient way, have to be revised!
continue;
#endif
}
pcPic->setOutputMark(false);
}
iterPic++;
}
}
/** \param pcListPic list of pictures to be written to file
\todo DYN_REF_FREE should be revised
*/
Void TAppDecTop::xFlushOutput( TComList<TComPic*>* pcListPic )
{
if(!pcListPic)
{
return;
}
TComList<TComPic*>::iterator iterPic = pcListPic->begin();
iterPic = pcListPic->begin();
while (iterPic != pcListPic->end())
{
TComPic* pcPic = *(iterPic);
TComSPS *sps = pcPic->getSlice(0)->getSPS();
if ( pcPic->getOutputMark() )
{
// write to file
if ( m_pchReconFile )
{
m_cTVideoIOYuvReconFile.write( pcPic->getPicYuvRec(), sps->getPicCropLeftOffset(), sps->getPicCropRightOffset(), sps->getPicCropTopOffset(), sps->getPicCropBottomOffset() );
}
// update POC of display order
m_iPOCLastDisplay = pcPic->getPOC();
// erase non-referenced picture in the reference picture list after display
if ( !pcPic->getSlice(0)->isReferenced() && pcPic->getReconMark() == true )
{
#if !DYN_REF_FREE
pcPic->setReconMark(false);
// mark it should be extended later
pcPic->getPicYuvRec()->setBorderExtension( false );
#else
pcPic->destroy();
pcListPic->erase( iterPic );
iterPic = pcListPic->begin(); // to the beginning, non-efficient way, have to be revised!
continue;
#endif
}
pcPic->setOutputMark(false);
}
iterPic++;
}
pcListPic->clear();
m_iPOCLastDisplay = -MAX_INT;
}