I am very new to programming, but have been following c++ tutorials and amassing a number of PDFs for the last week or so to help me out. I couldn't find anything in them or online that answered my question clearly enough. Please forgive me for my newbie-ness.
Pertinent code:
Logfile.hpp
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// HEADER :: CLASS INTERFACE FILE
// ==============================
// Include Guard
#ifndef __LOGFILE_INCLUDED__ // If Actor.hpp hasn't been included yet
#define __LOGFILE_INCLUDED__ // Define this for the compiler so it knows it has now been included
// ==============================
// Forward declared dependencies
// ==============================
// Included dependencies
#include <iostream>
#include <fstream>
// ==============================
// Actual class
class Logfile { // Logfile
public:
// Globally accessible variables
bool LACT; // Is log file active?
std::ofstream LOG; // Actual log file
std::string entry; // Data to be entered in log file
// ==============================
// Core methods
Logfile(); // Constructor
~Logfile(); // Destructor
// Additional methods
bool isActive(); // Is log file active?
void logEntry(std::string entry); // Make an entry in the log if 'LACT' is set to true
void toggleLog(); // Toggle log file open or closed
};
extern Logfile *log; // This variable is declared in main
#endif // __LOGFILE_INCLUDED__
Logfile.cpp
// ==============================
// Included dependencies
#include "Logfile.hpp"
// ==============================
// Core methods
Logfile::Logfile() { // Constructor
LACT = true; // Toggle logfile to active
LOG.open ("LOG.txt"); // Open 'log.txt' and prepare it to receive 'LOG' entries
LOG << "LOG FILE CLASS CONSTRUCTED\n";
LOG << "LOG FILE OPENED\n";
}
Logfile::~Logfile() { // Deconstructor
LOG << "LOG FILE CLOSED\n";
LOG << "LOG FILE CLASS DECONSTRUCTED";
LOG.close(); // Close log file
}
// Additional methods
bool Logfile::isActive() { // Is log file active?
if ( LACT ) return true;
else return false;
}
void Logfile::logEntry(std::string entry) { // Make an entry in the log if 'LACT' is set to true
if ( LACT ) LOG << entry << std::endl;
}
void Logfile::toggleLog() { // Toggle log file open or closed
if ( LACT ) { // Log file is active
LOG << "LOG FILE CLOSED\n";
LOG.close(); // Close log file
} else { // Log file is inactive
LOG.open ("LOG.txt"); // Open 'log.txt' and prepare it to receive 'LOG' entries
LOG << "LOG FILE OPENED\n";
}
}
Engine.hpp
// ==============================
// Forward declared dependencies
class Logfile;
class Engine { // Core system, main loop
public :
// Globally accessible variables
Logfile *log; // Declare 'log' as an empty pointer (*)
Engine.cpp
// ==============================
// Included dependencies
#include "Logfile.hpp"
// ==============================
// Core methods
Engine::Engine() { // Constructor method
// Initialization
log = new Logfile(); // Declare 'log' as pointer to access log file
TCODConsole::initRoot(80,50,"Testbed",false); // Create 'root' console (not fullscreen)
if ( log->isActive() ) log->logEntry("(TCODConsole) Root console initialized"); // WORKS
Map.hpp
// ==============================
// Forward declared dependencies
class Logfile;
extern Logfile *log; // Pointer exists in Engine.hpp
Map.cpp
// ==============================
// Included dependencies
#include "Logfile.hpp"
if ( log->isActive() ) log->logEntry("(TCODConsole) Root console initialized"); TERMINATION STATUS 3
if ( tiles[(x-1)+y*width].tType =="floor" ) tally++; // Left tile status, add 1 to tally if floor : TERMINATION STATUS 3
if ( tiles[(x-1)+(y-1)*width].canWalk ) tally++; // Left-top tile status, add 1 to tally if floor : WORKS
If I understand correctly, a termination status 3 indicates that I am referencing a variable incorrectly in regards to whether it's a pointer or not...? I initially ran into the problem when I wanted to access the tType string from an individual tile in the 2Darray in Map.cpp (though I can access the boolean variable canWalk just fine...), and couldn't figure out what was wrong, so I decided to learn to implement an external log to find the problem...but I guess I found my way back to the same issue while doing that...
Any help is greatly appreciated, as is criticism, I have a lot to learn.
--
My initial purpose for asking this question was (now I realize) to get a globally declared object accessible from any *.cpp file in a multi-file program. I just found this answer: http://www.cplusplus.com/forum/beginner/3848/, in case this might be helpful to anyone else with a similar problem.
In your Logfile.hpp you're missing #include <string>: one of your LogFile classes variables are declared std::string so you need to include that.
In your Engine.cpp you forget to include your Engine.hpp where your LogFile *log; variable is declared. this results in an error in your Engine.cpp file where you try assigning a new LogFile object to it.
So add #include <string> to the top of your Logfile.hpp and add #include "Engine.hpp" to the top of your Engine.cpp
Related
There is a "hello world" project in Eclipse IDE that is supposed to compile against ESP8266 RTOS SDK.
File structure is as follows
I added one C++ class to it and put it into its own folder. Here is the class header
#ifndef MAIN_BLINKER_BLINKER_H_
#define MAIN_BLINKER_BLINKER_H_
class Blinker {
public:
Blinker( int period );
int Period() const;
private:
int period_;
};
#endif /* MAIN_BLINKER_BLINKER_H_ */
and the definitions
#include "Blinker.h"
Blinker::Blinker( int period ) :
period_( period )
{}
int Blinker::Period() const {
return this->period_;
}
Main.cpp file is like this
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "blinker/Blinker.h"
extern "C" {
void app_main()
{
auto blnk = Blinker( 3000 );
int i = 0;
while ( 1 ) {
printf( "[%d] Hello beautiful world!\n", i );
i++;
vTaskDelay( blnk.Period() / portTICK_PERIOD_MS );
}
}
}
It compiles but fails at final stage because the linker (or what is supposed to be a linker in xtensa toolchain) does not see definitions of Blinker methods. This is what I get in the build log
If I put class files next to main.cpp file, the build succeeds. However with time there will be hundreds of files, and without any grouping it will quickly turn into an unmanageable mess.
Alternatively I could put this class into top-level components folder and equip it with empty component.mk file. This would also make the build system happy, however it would force me to use ugly header includes like ../components/blinker/Blinker.h, which I would like to avoid.
So the question is how to make build system aware of .c and .cpp files residing in subfolders of main folder?
you can set COMPONENT_SRCDIRS in "main" component.mk file
see:
https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/api-guides/build-system.html#example-component-makefiles
Try to add blinker/Blinker.cpp to your CMakeLists.txt.
Take a look at
How to add new source file in CMakeLists.txt?
I'm trying to summarize some experimental results in a file.
The results are generated in various C++ classes and files.
I want them to all write to the same file.
For this reason it would be conventient to have a header that defines everything, and then I can just include it in the files that need to write to it. I should be a singleton, so it is not tried to open the same file twice.
It looks like this:
#ifndef FILELOGGER_H
#define FILELOGGER_H
#include <fstream>
class FileLogger{
std::ofstream *logfile;
static FileLogger *s_instance;
FileLogger()
{
logfile = new std::ofstream();
logfile->open("~/results/experiments.txt", std::ios_base::app);
}
~FileLogger()
{
std::cout << "Destructor of logger called" << std::endl;
if(s_instance)
{
logfile->close();
delete logfile;
delete s_instance;
}
}
public:
static std::ofstream *instance()
{
if (!s_instance)
{
s_instance = new FileLogger();
}
std::cout << "got logger" << std::endl;
return s_instance->logfile;
}
};
FileLogger *FileLogger::s_instance = 0;
#endif // FILELOGGER_H
I would now think that in another file I just do:
#include "FileLogger.h"
and then use
*FileLogger::instance() << "Testoutput" << std::endl;
to write to the file. However, if I try it out, the file is not created; if I create it per hand nothing is written to it.
I do get the output of "got logger", that is called when the logger is accessed via the instance method.
I also noticed that the destructor is never called.
Why is this not working / Is this bad style?
This code
logfile->open("~/results/experiments.txt", std::ios_base::app);
tries to open a file with the literal name ~/results/experiments.txt. Tilde expansion to your home directory is done by your command shell (probably bash). You need to use the actual name of your home directory, for example:
logfile->open("/home/yourusername/results/experiments.txt", std::ios_base::app);
Let go of the 'header' trick.
Just declare a class in a header, implement it in a cpp file, and use a static counter or static boolean to prevent multiple instantiations.
I think that's much simpler than this header-only singleton magic.
Everything compiles file. But when I step through the code in Visual Studio 12, the line where Boost checks if the folder actually exists jumps the else statement. The folder is in the correct path (same path as the .exe), and if I remember correctly, the same code worked in Ubuntu without a problem. I've read through the Boost documentation and I can't really see what I'm doing wrong.
main.cpp
#include "game.hpp"
#include "texturemanager.hpp"
#include "spritemanager.hpp"
#include "imagemanager.hpp"
int main()
{
//Texture manager to hold all textures for the game.
TextureManager::GetInstance().LoadContent("Data/Images/Sprites");
//Sprite manager to hold all backgrounds used in the game
SpriteManager::GetInstance().LoadContent("Data/Images/Backgrounds");
//Create the game. Takes in the parameters of the size of the screen
Game game(640, 480);
game.Run();
}
texturemanager.hpp
#ifndef TEXTUREMANAGER_HPP
#define TEXTUREMANAGER_HPP
#include <SFML/Graphics.hpp>
#include <map>
#include <string>
#include <memory>
#include <assert.h>
#include <boost/filesystem.hpp>
class TextureManager
{
public:
typedef std::map<int, std::unique_ptr<sf::Texture>> resourceMap;
//For creating a singleton class
static TextureManager &GetInstance();
//Load files from the folder loaded from the settings file
void LoadContent(const std::string&);
//Load files from folder and with a specific file name
//To be added later to reduce memory usage
//void LoadContent(const std::string&);
//Get the texture
const sf::Texture& Get(int) const;
sf::Texture& Get(int);
protected:
private:
//These are needed to create a singleton class
TextureManager(){}
TextureManager(TextureManager const&){}
void operator=(TextureManager const&){}
//Add a file to the resource map
void Insert(boost::filesystem::path);
//Hold the textures in memory
resourceMap mTextureMap;
};
#endif
texturemanager.cpp
#include "texturemanager.hpp"
TextureManager& TextureManager::GetInstance()
{
static TextureManager instance;
return instance;
}
void TextureManager::LoadContent(const std::string& folder)
{
//Create a temp vector for the file entries to go to
std::vector<boost::filesystem::directory_entry> files;
//Create the directory where all sprites will be stored
boost::filesystem::path spriteDir(folder);
spriteDir.make_preferred();
//Check to see if the directory exists
if(boost::filesystem::exists(spriteDir))
{
//Copy all of the entries into the directory vector
copy(boost::filesystem::directory_iterator(spriteDir),
boost::filesystem::directory_iterator(),
back_inserter(files));
}
else
{
std::cout << folder + " Does not exist" << std::endl;
}
//Check to see if anything was stored
if(files.size() > 0)
{
//Loop through each of the files
for(unsigned int i = 0; i < files.size(); i++)
{
if(files[i].path().extension() == ".png")
{
//Convert the file to a string and push into vector
Insert(files[i].path());
}
}
//Empty the vector for reuse
files.clear();
}
}
void TextureManager::Insert(boost::filesystem::path tempFile)
{
//Create and load resource
std::unique_ptr<sf::Texture> tempTexture(new sf::Texture());
if(!tempTexture->loadFromFile(tempFile.string()))
throw std::runtime_error("ResourceHolder::load - Failed to load " + tempFile.string());
std::string srtID = tempFile.stem().string();
int id = std::atoi(srtID.c_str());
//Insert and check success
auto inserted = mTextureMap.insert(std::make_pair(id, std::move(tempTexture)));
assert(inserted.second);
}
Relative paths are not relative to the executable itself, but relative to the working directory. So if you double click the exe the working directory will be set correctly to the same location as your executable, but if you start it in Visual Studio, it will use the working directory set in the settings. You find the option in your project settings under Debugging->Working Directory:
Unrelated to the question at hand, I strongly discourage you from using the singleton pattern for your texture manager, instead you should choose a design that doesn't require access to the manager from everywhere and reduce the dependencies as much as possible, thus making passing by const reference a good/clean way to go.
I am currently learning how to write my own library for my mbed electronics project. So far I have two files cfExtensions.cpp and cfExtensions.h files. I referenced my variables in the cfExtensions.h constructor and changed their values within my cfExtensions.cpp file; however my mbed c++ compiler throws: identifier "phMin" is unidentified. My code is:
FILE: cfExtensions.h
/*
File: cfExtensions.h
Header file for cfExtensions Library.
*/
#ifndef __cfExtns_H
#define __cfExtns_H
#include "mbed.h"
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
//==================================
// Definitions
//==================================
#define CF_FILE_LOCATION "local/settings.cf" // File location local/settings.cf
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
//==================================
// cfExtension Class
//==================================
class cfExtensions
{
public:
//---------------------------
// Function Prototypes
//---------------------------
cfExtensions(); // Constructor, Initialisation tasks
void loadConfigFile(); // Loads config file defined in CF_FILE_LOCATION
void checkConfigForFirstStart(); // Check if MBED startup is the very first startup
void getPhMaxValueFromConfigFile(); // Get PH Max value from config file
void getPhMinValueFromConfigFile(); // Get PH Min value from config file
void getKeyAndValue();
//---------------------------
// Variables
//---------------------------
volatile bool pingTicked;
bool linkedWithBaseStation;
char *sbNameKey;
char sbNameValue[BUFSIZ];
char *sbFirstStartKey;
char sbFirstStartValue[BUFSIZ];
char *sbUseBaseStationKey;
char sbUseBaseStationValue[BUFSIZ];
char *sbPhMaxKey;
char sbPhMaxValue[BUFSIZ];
char *sbPhMinKey;
char sbPhMinValue[BUFSIZ];
float phMax;
float phMin;
//---------------------------
// Devices
//---------------------------
};
#endif
FILE: cfExtensions.cpp
//================================
// Get PH Min Value from CF
//================================
void getPhMinValueFromConfigFile() {
/*
* Get a configuration value.
* Then attach the sbNameValue to SensorData json
*/
if (cfg.getValue(sbPhMinKey, &sbPhMinValue[0], sizeof(sbPhMinValue))) {
phMin = atof(sbPhMinValue);
}
} // End of getPhMinValueFromConfigFile
I think it should be void cfExtensions::getPhMinValueFromConfigFile() { } in your cfExtensions.cpp file.
In the cpp file change the function implementation to
void cfExtensions::getPhMinValueFromConfigFile() {
// etc ....
}
The key here is to have cfExtensions:: in front of the function.
I tried the exemple from pantheios to log to a file but can't manage to make it work.
Messages are correctly displayed in the console but the log file isn't created.
I tried to change severity levels since I saw that thread, but no one works.
Here's the code :
/* Pantheios Header Files */
#include <pantheios/pantheios.hpp> // Pantheios C++ main header
#include <pantheios/inserters/args.hpp> // for pantheios::args
#include <pantheios/inserters/exception.hpp> // for pantheios::exception
#include <pantheios/backends/bec.file.h> // be.file header
/* Standard C/C++ Header Files */
#include <exception> // for std::exception
#include <new> // for std::bad_alloc
#include <string> // for std::string
#include <stdlib.h> // for exit codes
/* ////////////////////////////////////////////////////////////////////// */
/* Define the stock front-end process identity, so that it links when using
* fe.N, fe.simple, etc. */
PANTHEIOS_EXTERN_C const PAN_CHAR_T PANTHEIOS_FE_PROCESS_IDENTITY[] = PANTHEIOS_LITERAL_STRING("example.cpp.file");
/* ////////////////////////////////////////////////////////////////////// */
#define PSTR(x) PANTHEIOS_LITERAL_STRING(x)
/* ////////////////////////////////////////////////////////////////////// */
int main(int argc, char **argv)
{
try
{
#ifndef PANTHEIOS_USE_WIDE_STRINGS
pantheios::log_DEBUG("main(", pantheios::args(argc, argv), ")");
#else /* ? !PANTHEIOS_USE_WIDE_STRINGS */
STLSOFT_SUPPRESS_UNUSED(argc); STLSOFT_SUPPRESS_UNUSED(argv);
#endif /* !PANTHEIOS_USE_WIDE_STRINGS */
pantheios::log_NOTICE(PSTR("stmt 1"));
// Set the file name for the local back-end, truncating the
// file's existing contents, if any.
pantheios_be_file_setFilePath(PSTR("log.local"), PANTHEIOS_BE_FILE_F_TRUNCATE, PANTHEIOS_BE_FILE_F_TRUNCATE, PANTHEIOS_BEID_LOCAL);
pantheios::log_NOTICE(PSTR("stmt 2"));
// Set the file name for the remote back-end.
pantheios_be_file_setFilePath(PSTR("log.remote"), PANTHEIOS_BEID_REMOTE);
pantheios::log_NOTICE(PSTR("stmt 3"));
// Set the file name for all back-ends.
pantheios_be_file_setFilePath(PSTR("log.all"));
pantheios::log_NOTICE(PSTR("stmt 4"));
pantheios::log_DEBUG(PSTR("exiting main()"));
system("pause");
return EXIT_SUCCESS;
}
catch(std::bad_alloc&)
{
pantheios::log(pantheios::alert, PSTR("out of memory"));
}
catch(std::exception& x)
{
pantheios::log_CRITICAL(PSTR("Exception: "), pantheios::exception(x));
}
catch(...)
{
pantheios::logputs(pantheios::emergency, PSTR("Unexpected unknown error"));
}
return EXIT_FAILURE;
}
/* ///////////////////////////// end of file //////////////////////////// */
I have an "include_pantheios.cpp" file for implicit link purpose. Here it is :
/* Pantheios Header Files */
#include <pantheios/implicit_link/core.h>
#include <pantheios/implicit_link/fe.simple.h>
#include <platformstl/platformstl.h>
#include <pantheios/implicit_link/be.file.h>
#if ( defined(UNIX) || \
defined(unix))&& \
( defined(_WIN32) || \
defined(_WIN64))
# include <unixem/implicit_link.h>
#endif /* _WIN32 || _WIN64 */
Does somebody see where my problem come from?
Thanks in advance,
Vincent
I think part of your confusion comes from the example doing too much: it shows local and remote files all in one. A simpler example would be:
// Headers for main()
#include <pantheios/pantheios.hpp>
#include <pantheios/backends/bec.file.h>
// Headers for implicit linking
#include <pantheios/implicit_link/core.h>
#include <pantheios/implicit_link/fe.simple.h>
#include <pantheios/implicit_link/be.file.h>
int main() {
pantheios::log_NOTICE("log-1"); // save until log file set
pantheios_be_file_setFilePath("mylogfile"); // sets log file; write "log-1" stmt
pantheios::log_NOTICE("log-2"); // write "log-2" stmt
pantheios_be_file_setFilePath(NULL); // close "mylogfile"
pantheios::log_NOTICE("log-3"); // save until log file set
pantheios_be_file_setFilePath("mylogfile2"); // sets log file; write "log-3" stmt
pantheios::log_NOTICE("log-4"); // write "log-4" stmt
} // closes "mylogfile2" during program closedown
The problem with the original code, which I think comes from a Pantheios example program, is that it's trying to illustraet how to use local and remote back-ends at the same time as trying to illusteate how to use the be.file backend.
Forget all the different back-ends, and concentrate on the be.file-specific stuff.
HTH
I got the same problem, for future people, the problem is the order to link libraries
Pantheios Forum :
https://sourceforge.net/projects/pantheios/forums/forum/475314/topic/5313841/index/page/1
I just link the pantheios.1.be.file.gcc44 before the pantheios.1.be.fprintf.gcc44
I think the issue is the order in which you link, but I don't quite see how it's possible given the code you posted.
I encountered the same issue, and I realized that it was because I was linking two backends at once: file and fprintf. More specifically, it was because I was linking fprintf before file. When I switched the order to link file first, then it would create and use the log file, but would not output to stdout when I commented out pantheios_be_file_setFilePath. So apparently whichever is linked first is the only one that will work (look up multiple backends).
As far as I can tell this code is identical to the file stock back-end sample given with the library, so it ought to work.
How are you determining that the log files are not written? These are relative paths - try using absolute paths to be sure you are looking in the correct place.
If all else fails, you could debug through the code (after the filepath is set) to find out why nothing is getting written out.