This question already has answers here:
multiple definition of a function
(3 answers)
Multiple definition and header-only libraries
(4 answers)
Closed 1 year ago.
I am having trouble in including a header only library. In my main Application when I include the header file it works fine, now when I need to reference some of the classes from it in a different file then I get multiple definition errors. I checked the header only file has #pragma once and #ifndef checks added
Here is the code (webview.h) which is a header only library included here.
Window.h
#pragma once
#include <memory>
#include <filesystem>
#include <webview.h>
namespace HL {
struct Props {
int width;
int height;
std::string title;
Props(const std::string& title = "My Test Application",
uint32_t width = 1600,
uint32_t height = 900)
: title(title), width(width), height(height)
{
}
};
class Window {
public:
Window(const Props& props)
{
init(props);
}
static Window* create(const Props props)
{
return new Window(props);
}
~Window()
{
terminate();
}
void terminate()
{
mWebview.terminate();
}
void init(const Props& props )
{
mProps.width = props.width;
mProps.height = props.height;
mProps.title = props.title;
mWebview = webview::webview(true, nullptr);
mWebview.set_title(mProps.title);
mWebview.set_size(1080, 800, WEBVIEW_HINT_NONE);
std::string uiFile = std::filesystem::current_path().u8string() + std::string("/ui/build/index.html");
mWebview.navigate(std::string("file://") + uiFile);
mWebview.run();
}
private:
webview::webview mWebview;
int mWidth = 1080;
int mHeight = 800;
std::string mTitle = "";
Props mProps;
};
};
Application.h
#pragma once
#include <Focus.h>
#include <string>
#include <memory>
#include "Window.h"
namespace HL
{
class Application : public Focus::Application
{
private:
Window *mWindow;
public:
Application(Focus::Sqlite* db): BaseApplication(db)
{
FOCUS_LOG_INFO("Initializing DB");
db->init();
}
void run()
{
startClients();
mWindow = Window::create(Props("Test Application"));
}
Window& getWindow()
{
return *mWindow;
}
void loadPlugins()
{
}
};
}
EventManager.h
Here is when I include Window.h which has webview.h include I get the linker errors
#pragma once
#include <memory>
#include <sqlite3.h>
#include "Event.h"
#include <string>
#include <json.hpp>
#include <sstream>
#include "Window.h"
using json = nlohmann::json;
namespace HL
{
class EventManager
{
private:
Window mWindow;
sqlite3* mDB;
public:
EventManager(Window& window, sqlite3* db) : mWindow(window), mDB(db) {}
void bindAll();
void persist(Event& event);
};
};
And the errors
/usr/bin/ld: CMakeFiles/hl.dir/src/main.cpp.o: in function `webview_create':
/home/naveed/Projects/hl/desktop/vendor/webview/webview.h:1305: multiple definition of `webview_create'; CMakeFiles/hl.dir/src/EventManager.cpp.o:/home/naveed/Projects/hl/desktop/vendor/webview/webview.h:1305: first defined here
/usr/bin/ld: CMakeFiles/hl.dir/src/main.cpp.o: in function `webview_destroy':
Related
I've been trying to compile my project and I've encountered some problems when trying so. The error in particular that appears is:
[build] /usr/bin/ld: CMakeFiles/robot_control.dir/main.cpp.o:(.data.rel.ro._ZTVN4comm15cameraInterfaceE[_ZTVN4comm15cameraInterfaceE]+0x10): undefined reference to `comm::Interface<cv::Mat>::callbackMsg()'
My project is organized right now as it follows:
-${HOME_WORKSPACE}
|-main.cpp
|-src
|-communication.cpp
|-communication.hpp
The header file (communication.hpp) is:
#include <opencv2/opencv.hpp>
#include <gazebo/gazebo_client.hh>
#include <gazebo/msgs/msgs.hh>
#include <gazebo/transport/transport.hh>
#include <algorithm>
#ifndef COMM_GUARD
#define COMM_GUARD
namespace comm
{
struct lidarMsg
{
float angle_min, angle_increment, range_min, range_max;
int nranges, nintensities;
std::vector<int> ranges;
};
template <typename T>
class Interface
{
public:
Interface() : received{false} {};
virtual void callbackMsg();
bool receptionAccomplished()
{
return this -> received;
}
T checkReceived()
{
return this -> elementReceived;
}
protected:
bool received;
T elementReceived;
};
class cameraInterface : public Interface<cv::Mat>
{
public:
void callbackMsg(ConstImageStampedPtr &msg);
};
class lidarInterface : public Interface<lidarMsg>
{
public:
void callbackMsg(ConstLaserScanStampedPtr &msg);
};
}
#endif
The source file (communication.cpp) is:
#include <opencv2/opencv.hpp>
#include <algorithm>
#include <iostream>
#include "communication.hpp"
#ifndef COMM_CPP_GUARD
#define COMM_CPP_GUARD
namespace comm
{
void cameraInterface::callbackMsg(ConstImageStampedPtr &msg)
{
std::size_t width = msg->image().width();
std::size_t height = msg->image().height();
const char *data = msg->image().data().c_str();
cv::Mat im(int(height), int(width), CV_8UC3, const_cast<char *>(data));
im = im.clone();
cv::cvtColor(im, im, cv::COLOR_RGB2BGR);
this->elementReceived = im;
received = true;
}
void lidarInterface::callbackMsg(ConstLaserScanStampedPtr &msg) {
this->elementReceived.angle_min = float(msg->scan().angle_min());
this->elementReceived.angle_increment = float(msg->scan().angle_step());
this->elementReceived.range_min = float(msg->scan().range_min());
this->elementReceived.range_max = float(msg->scan().range_max());
this->elementReceived.nranges = msg->scan().ranges_size();
this->elementReceived.nintensities = msg->scan().intensities_size();
for (int i = 0; i < this->elementReceived.nranges; i++)
{
if (this->elementReceived.ranges.size() <= i)
{
this->elementReceived.ranges.push_back(std::min(float(msg->scan().ranges(i)), this->elementReceived.range_max));
}
else
{
this->elementReceived.ranges[i] = std::min(float(msg->scan().ranges(i)), this->elementReceived.range_max);
}
}
}
}
#endif
The main file(main.cpp) includes the following header:
#include <gazebo/gazebo_client.hh>
#include <gazebo/msgs/msgs.hh>
#include <gazebo/transport/transport.hh>
#include <opencv2/opencv.hpp>
#include <opencv2/calib3d.hpp>
#include <iostream>
#include <stdlib.h>
#include "src/communication.hpp"
I included the part of the #ifndef /#define /#endif since it is a solution that I found to this kind of problem in other problem. I've been toggling the CMakeLists.txt file but still no solution that could solve this error.
You can't do this:
virtual void callbackMsg();
You have to actually provide the implementation for all template methods within the .h file.
I am new to C++, and I am currently working on 2 different projects within the some solution in C++ with VSC and I am encountering the error "A class-qualified name is required"; I have created a custom class in a file inside a namespace like so in project Stock:
//in file Stock.h, project Stock
#pragma once
#include "CandleStick.h"
#include "StockRecord.h"
#include "MarketQue.h"
#include <string>
#include <vector>
namespace Stock {
class Stock {
private:
std::string s_stockID;
MarketQue* s_topCur;
StockRecord s_stockRecord;
std::vector<CandleStick> s_candleSticks;
unsigned int s_totalVol;
public:
Stock(const std::string&, MarketQue&, std::vector<unsigned int>&, unsigned int); //stockID, s_topCurPrice_B, s_topCurPrice_S, s_topCurVol_B, s_topCurVol_S, cs_interval, total_vol
void updateStock(int);
~Stock();
protected:
};
}
//in file Stock.cpp, project Stock
#include "Stock.h"
Stock::Stock::Stock(const std::string& stockID, MarketQue& topPrices, std::vector<unsigned int>& csInterval, unsigned int totalVol)
:s_stockID(stockID), s_topCur(&topPrices), s_stockRecord(StockRecord(topPrices.mq_size)), s_totalVol(totalVol) {
s_candleSticks.reserve(csInterval.size());
for (unsigned int i = 0; i < csInterval.size(); i++) {
s_candleSticks.emplace_back(topPrices.mq_topPrice_S[0], topPrices.mq_topPrice_B[0], csInterval[i]);
}
};
void Stock::Stock::updateStock(int totalVol) {
s_totalVol = totalVol;
s_stockRecord.updateStockRecord(*s_topCur);
for (unsigned int i = 0; i < s_candleSticks.size(); i++) {
s_candleSticks[i].updateCandleStick((s_topCur->mq_topPrice_B)[0], (s_topCur->mq_topPrice_S)[0]);
}
}
Stock::Stock::~Stock() {};
I have successfully linked the 2 projects together (I think, tested it in the main function with a file called App.cpp in project Market):
//in file App.cpp, project Market
#include "Stock.h"
#include "MarketQue.h"
#include <string>
#include <array>
#include <vector>
int main() {
//initializing
const std::string stockID = "001";
Stock::MarketQue topPrices(3);
std::vector<float> topCurPrice_B = { 3,2,1 };
std::vector<float> topCurPrice_S = { 4,5,6 };
topPrices.mq_topPrice_B = topCurPrice_B;
topPrices.mq_topPrice_S = topCurPrice_S;
std::vector<unsigned int> topCurVol_B = { 10,11,12 };
std::vector<unsigned int> topCurVol_S = { 13,14,15 };
topPrices.mq_topVol_B = topCurVol_B;
topPrices.mq_topVol_S = topCurVol_S;
std::vector<unsigned int> cs_Interval = { 100,300,1000 };
unsigned int totalVol= 3+2+1+4+5+6;
Stock::Stock first_stock(stockID, topPrices, cs_Interval, totalVol);
totalVol = 100;
first_stock.updateStock(totalVol);
//testing update
topCurPrice_B = { 13,12,11 };
topCurPrice_S = { 14,15,16 };
topCurVol_B = { 110,111,112 };
topCurVol_S = { 113,114,115 };
topPrices.mq_topPrice_B = topCurPrice_B;
topPrices.mq_topPrice_S = topCurPrice_S;
topPrices.mq_topVol_B = topCurVol_B;
topPrices.mq_topVol_S = topCurVol_S;
}
However, when I try to use this class(Stock::Stock) as a member inside of another class (in project Market), I received the error mentioned above ("A class qualified name is required");
I first did this:
//in file Market.h, project Market
#pragma once
#include "Stock.h"
#include <string>
#include <vector>
namespace Market {
class Market {
private:
std::string marketID;
std::vector<Stock::Stock> stock; //received error
Stock::Stock stock;
public:
Market();
void updateStock();
~Market();
protected:
};
}
At this point i figured it might be because of the namespace Market making it Market::Stock::Stock and i decided to remove the namespace Market:
//in file Market.h, in project Market
#pragma once
#include "Stock.h"
#include <string>
#include <vector>
class Market {
private:
std::string marketID;
std::vector<Stock::Stock> stock;
public:
Market();
void updateStock();
~Market();
protected:
};
I'd like to ask how to fix this problem, and if possible can someone explain to me what is happenning to the code that caused the error?
Thank you very much for your time.
Why this in Client.cpp file I'm getting error C2065:'TunnelContainer': undefined identifier?
Client.cpp code:
#include "stdafx.h"
#include "GClientLib.h"
using namespace GClientLib;
int _tmain(int argc, _TCHAR* argv[])
{
SettingsReader^ settings = gcnew SettingsReader();
SocketToObjectContainer^ STOContainer = gcnew SocketToObjectContainer();
TunnelContainer^ tunnels = gcnew TunnelContainer();
timeval time;
time.tv_sec = 0;
time.tv_usec = 300000;
....
GClientLib.h code fragment:
#include "Structures.h"
#include "Globals.h"
#include "SettingsReader.h"
#include "SocketToObjectContainer.h"
#include "SocketToSocketContainer.h"
#include "TunnelContainer.h"
Updated. SocketToSocketContainer.h
#ifndef SocketToSocketContainer_H
#define SocketToSocketContainer_H
#include <cliext/utility>
#include <cliext/list>
#include <cliext/algorithm>
namespace GClientLib {
ref class SocketToSocketContainer {
private:
cliext::list<cliext::pair<int, int>> sarasas;
public:
SocketToSocketContainer(void);
void Add(int, int);
int Find(int);
void Delete(int);
};
};
#endif
GclientLib is lib project, used in Client application. Build on Visual Studio 2013 C++/CLI enabled
TunnelContainer.h code:
#ifndef GClientLib_H
#define GClientLib_H
#include <cliext/utility>
#include <cliext/list>
#include <cliext/algorithm>
namespace GClientLib {
enum TunnelStatus
{
JUNGIASI = 1, //Uzmezgamas rysys tarp klientu
LAUKIA_PROGRAMOS = 2, // Laukia kol prisijungs norima kliento porgramine iranga
KOMUNIKACIJA = 3 // Tuneliu vyksta komunikacija
};
ref struct Tunnel
{
int tag; //Tunelio zyme
int dport; //Prievadas, prie kurio jungesi
int clientid; //Kliento ID su kuriuo sujungta
int sport; //Vietinis prievadas
int serverSocket; //Socketas, prie kuris priima duomenu srauta
int status; // Sujungimo statusas (Jungiasi, prisjungta, laukia jungties)
};
ref class TunnelContainer {
private:
// Tuneliu sarasas
cliext::list<cliext::pair<int, Tunnel^>> sarasas;
public:
// Konstruktorius
TunnelContainer();
// Pridedamas naujas tunelis. PERRASO statusa i JUNGIAMASI
Tunnel^ Add(Tunnel^ tunelis);
// Pridedamas naujas tunelis. Statusa nustato i JUNGIAMASI
Tunnel^ Add(int tag, int dport, int clientid, int sport, int serverSocket);
// Tunelio paieska pagal tag
Tunnel^ Find(int tag);
// Salina tuneli pagal tag
Tunnel^ Remove(int tag);
// Keicia tunelio statusa
void ChangeStatus(int tag, TunnelStatus status);
};
};
#endif
UPDATE
After moving TunnelContainer.h into first position getting this error in ToServerSocket.h file:
Error 1 error C2143: syntax error : missing ';' before '^' line 16
Error 2 error C4430: missing type specifier - int assumed. Note: C++ does not support default-int line 16
ToServerSocket.h code:
#ifndef ToServerSocket_H
#define ToServerSocket_H
#include <iostream>
#include "gNetSocket.h"
#include "ServerSocket.h"
#include "OutboundSocket.h"
namespace GClientLib {
ref class ToServerSocket : public gNetSocket {
private:
char *commandBuffer;
line 16 --->TagGenerator^ tag;
SocketToObjectContainer^ STOC;
SettingsReader^ settings;
public:
ToServerSocket(string ip, string port, fd_set* skaitomiSocket, fd_set* rasomiSocket, fd_set* klaidingiSocket, SocketToObjectContainer^ STOC, SettingsReader^ settings);
virtual int Send(char* data, int lenght) override;
virtual void Recive(SocketToObjectContainer^ container) override;
virtual void Connect() override;
virtual void Reconnect() override;
void CommandList(int page);
void CommandListAck(int rRecv);
void CommandHello();
void CommandHelp();
void CommandInitConnect(int id, int port, SocketToObjectContainer^ container);
void CommandConnect(SocketToObjectContainer^ container);
void CommandClear();
void CommandBeginRead(SocketToObjectContainer^ container);
void CommandClientConnectAck(SocketToObjectContainer^ container);
void CommandInitConnectAck();
void CommandJsonList(int page, SOCKET socket);
void CommandJsonListAck(int rRecv, SocketToObjectContainer^ container);
void CommandJsonInitConnect(int id, int port, SOCKET socket);
void CommandJSONConnect(SocketToObjectContainer^ container);
void CommandJsonInitConnectAck();
int GenerateTag();
};
};
#endif
you probably have a syntax error or missing ; or missing closing " in the include before that, "SocketToSocketContainer.h".
#include is a precompiler statement, all it does is include the content of the given file into the main file; it is your task to make sure that the result is a valid code. So if one include has an incomplete statement, the content of the second include continues that statement.
Edit: It could be even further up in the list - any open or incorrect #IFDEF in any include could remove the whole rest
I have found my mistake. GClientLib_H was already defined.
#ifndef GClientLib_H
#define GClientLib_H
Changed to
#ifndef TunnelContainer_H
#define TunnelContainer_H
Now everything is working. Thank you for quick responses
I've got a third party library named person.lib and its header person.h. This is my actual project structure and it compiles and runs perfectly.
Actual Structure:
main.cpp
#include <iostream>
#include <time.h>
#include <ctype.h>
#include <string>
#include "person.h"
using namespace person;
using namespace std;
class Client : public Person
{
public:
Client();
void onMessage(const char * const);
private:
void gen_random(char*, const int);
};
Client::Client() {
char str[11];
gen_random(str, 10);
this->setName(str);
}
void Client::onMessage(const char * const message) throw(Exception &)
{
cout << message << endl;
}
void Client::gen_random(char *s, const int len) {
//THIS FUNCTION GENERATES A RANDOM NAME WITH SPECIFIED LENGTH FOR THE CLIENT
}
int main()
{
try
{
Person *p = new Client;
p->sayHello();
}
catch(Exception &e)
{
cout << e.what() << endl;
return 1;
}
return 0;
}
I want to refactor my code by dividing the declaration of my Client class from its definition and create client.h and client.cpp. PAY ATTENTION: sayHello() and onMessage(const * char const) are functions of the person library.
Refactored Structure:
main.cpp
#include <iostream>
#include "client.h"
using namespace person;
using namespace std;
int main()
{
try
{
Person *p = new Client;
p->sayHello();
}
catch(Exception &e)
{
cout << e.what() << endl;
return 1;
}
return 0;
}
client.cpp
#include "client.h"
using namespace person;
using namespace std;
Client::Client() {
char str[11];
gen_random(str, 10);
this->setName(str);
}
void Client::onMessage(const char * const message) throw(Exception &)
{
cout << message << endl;
}
void Client::gen_random(char *s, const int len) {
//THIS FUNCTION GENERATES A RANDOM NAME WITH SPECIFIED LENGTH FOR THE CLIENT
}
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <time.h>
#include <ctype.h>
#include <string>
#include "person.h"
class Client : public Person
{
public:
Client();
void onMessage(const char * const);
private:
void gen_random(char*, const int);
};
#endif
As you can see, I've simply created a client.h in which there's the inclusion of the base class person.h, then I've created client.cpp in which there's the inclusion of client.h and the definitions of its functions. Now, the compilation gives me these errors:
error C2504: 'Person': base class undefined client.h 7 1 Test
error C2440: 'inizialization': unable to convert from 'Client *' to 'person::impl::Person *' main.cpp 15 1 Test
error C2504: 'Person': base class undefined client.h 7 1 Test
error C2039: 'setName': is not a member of 'Client' client.cpp 8 1 Test
error C3861: 'sendMessage': identifier not found client.cpp 34 1 Test
It's a merely cut© refactoring but it doesn't work and I really don't understand WHY! What's the solution and why it gives me these errors? Is there something about C++ structure that I'm missing?
Here's a dog-n-bird implementation (ruff ruff, cheep cheep)
cLawyer is defined and implemented in main.cpp, while cPerson and cClient are defined in their own header files, implemented in their own cpp file.
A better approach would store the name of the class. Then, one wouldn't need to overload the speak method - one could simply set the className in each derived copy. But that would have provided in my estimates, a less useful example for you.
main.cpp
#include <cstdio>
#include "cClient.h"
class cLawyer : public cPerson
{
public:
cLawyer() : cPerson() {}
~cLawyer() {}
void talk(char *sayWhat){printf("cLawyer says: '%s'\n", sayWhat);}
};
int main()
{
cPerson newPerson;
cClient newClient;
cLawyer newLawyer;
newPerson.talk("Hello world!");
newClient.talk("Hello world!");
newLawyer.talk("Hello $$$");
return 0;
}
cPerson.h
#ifndef cPerson_h_
#define cPerson_h_
class cPerson
{
public:
cPerson();
virtual ~cPerson();
virtual void talk(char *sayWhat);
protected:
private:
};
#endif // cPerson_h_
cPerson.cpp
#include "cPerson.h"
#include <cstdio>
cPerson::cPerson()
{
//ctor
}
cPerson::~cPerson()
{
//dtor
}
void cPerson::talk(char *sayWhat)
{
printf("cPerson says: '%s'\n",sayWhat);
}
cClient.h
#ifndef cClient_h_
#define cClient_h_
#include "cPerson.h"
class cClient : public cPerson
{
public:
cClient();
virtual ~cClient();
void talk(char *sayWhat);
protected:
private:
};
#endif // cClient_h_
cClient.cpp
#include "cClient.h"
#include <cstdio>
cClient::cClient()
{
//ctor
}
cClient::~cClient()
{
//dtor
}
Output
cPerson says: 'Hello world!'
cClient says: 'Hello world!'
cLawyer says: 'Hello $$$'
Suggestions noted above:
//In the cPerson class, a var
char *m_className;
//In the cPerson::cPerson constructer, set the var
m_className = "cPerson";
//Re-jig the cPerson::speak method
void cPerson::speak(char *sayWhat)
{
printf("%s says: '%s'\n", m_className, sayWhat);
}
// EDIT: *** remove the speak methods from the cClient and cLawyer classes ***
//Initialize the clas name apporpriately in derived classes
//cClient::cClient
m_className = "cClient";
//Initialize the clas name apporpriately in derived classes
//cLaywer::cLaywer
m_className = "cLawyer";
You are declaring the class Client twice - once in the .h file and once in .cpp. You only need to declare it in the .h file.
You also need to put the using namespace person; to the .h file.
If class Person is in namcespace person, use the person::Person to access it.
The client.cpp must contain definitions only!
I think for the linker the class Client defined in client.h and class Client defined in client.cpp are different classes, thus it cannot find the implementation of Client::Client(). I purpose to remove the declaration of class Client from the client.cpp and leave there only definitions of functions:
// client.cpp
#include <time.h>
#include <ctype.h>
#include <string>
#include "client.h"
using namespace std;
Client::Client()
{
//DO STUFF
}
void Client::onMessage(const char * const message)
{
//DO STUFF
}
void Client::gen_random(char *s, const int len) {
//DO STUFF
}
This question already has answers here:
How do I create a header-only library?
(4 answers)
Closed 9 years ago.
In a file called gl_ext.h I have the following:
#ifndef GLEXT_H_INCLUDED
#define GLEXT_H_INCLUDED
#include <stdexcept>
#ifdef WIN32
#include <Windows.h>
#include <GL/GL.h>
#include <GL/glext.h>
# define glGetProcAddress(arg) wglGetProcAddress(arg)
#elif __linux__
#include <GL/gl.h>
#include <GL/glext.h>
# include <GL/glx.h>
# define glGetProcAddress(arg) glXGetProcAddress((const GLubyte*)arg)
#endif
PFNGLCREATESHADERPROC glCreateShader = 0;
namespace glext
{
bool load_gl_extensions()
{
static bool loaded = false;
if (loaded) {
return true;
}
if (!glCreateShader) {
glCreateShader =
(PFNGLCREATESHADERPROC)(glGetProcAddress("glCreateShader"));
if (!glCreateShader) {
throw "Failed to load glCreateShader";
}
}
}
}
#endif
When building from within qt creator using the following .pro file
QT += core gui opengl
TEMPLATE = app
TARGET = GLExtensions
INCLUDEPATH += .
LIBS += -lGL
HEADERS += gl_ext.h \
qtrenderer.h
SOURCES += main.cpp \
qtrenderer.cpp
The usage of this "header library" is as follows:
main.cpp
#include <QtGui/QApplication>
#include "qtrenderer.h"
int main(int argc, char * argv[]) {
QApplication app(argc, argv);
QtRenderer *renderer = new QtRenderer();
renderer->show();
app.exec();
}
qtrenderer.h
#ifndef QTRENDERER_H_INCLUDED
#define QTRENDERER_H_INCLUDED
#include <QtCore/QObject>
#include <QtOpenGL/QGLWidget>
#include <gl_ext.h>
class QtRenderer : public QGLWidget
{
Q_OBJECT
private:
QtRenderer(const QtRenderer &other);
QtRenderer &operator = (const QtRenderer &other);
protected:
virtual void paintGL();
virtual void initializeGL();
public:
QtRenderer();
~QtRenderer();
public slots:
virtual void updateGL();
};
#endif
qtrenderer.cpp
#include "qtrenderer.h"
QtRenderer::QtRenderer() :
QGLWidget() {
}
QtRenderer::~QtRenderer() {
}
void QtRenderer::initializeGL() {
try {
glext::load_gl_extensions();
} catch (...) {
throw std::runtime_error("Failed to load needed extensions.");
}
}
void QtRenderer::paintGL() {
swapBuffers();
}
void QtRenderer::updateGL() {
paintGL();
}
When building this source code using
gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)
I get the following build errors:
qtrenderer.o: In function `glext::load_gl_extensions()':
/home/mehoggan/Devel/test_gl/./gl_ext.h:28: multiple definition of `glCreateShader'
main.o:/home/mehoggan/Devel/test_gl/./gl_ext.h:28: first defined here
Why is this so?
Well, the header gl_ext.h is included multiple times. Remember that #include is like replacing the #include statement with the content of the file in a copy&paste manner.
You should put the implementation of load_gl_extensions() into a .cpp file, and put only the declaration into the header file.
gl_ext.h:
//...
extern PFNGLCREATESHADERPROC glCreateShader;
namespace glext
{
bool load_gl_extensions();
}
gl_ext.cpp:
#include "gl_ext.h"
PFNGLCREATESHADERPROC glCreateShader = 0;
namespace glext
{
bool load_gl_extensions()
{
static bool loaded = false;
if (loaded) {
return true;
}
if (!glCreateShader) {
glCreateShader =
(PFNGLCREATESHADERPROC)(glGetProcAddress("glCreateShader"));
if (!glCreateShader) {
throw "Failed to load glCreateShader";
}
}
}
}
extern tells the compiler that the variable/function pointer (glCreateShader) is placed in a different compilation unit (every .cpp file is compiled as a different unit). The linker then inserts the correct memory-adress of your variable. Maybe you should do some research on how C++ compilation and linkage works.
The solution to the problem consisted of making the method inlined and declaring the function pointer as static.