I have a simple form, and I'm trying to work with the ProgressBar when a .zip file is being created, based on the ZipDirectoryContents callback. I have seen some Delphi example in the forum, but I don't know it well, and I get several errors. Here an example:
.cpp
#include <system.zip.hpp>
String PreviousFilename;
bool __fastcall createZipFromFolder( String inZipName,
String inSourcePath,
String inTargetPath )
{
bool result = true;
sZipName = "C:\\test\\testBar.zip";
sSourceName = "C:\\test2";
TZipFile* Zip = new TZipFile();
try{
if ( FileExists( sZipName ) ){
Zip->ZipDirectoryContents( sZipName, // destiny
sSourceName, // to include
zcDeflate, // compression mode
OnZipProgressEvent() ); // <--- p callback
Zip->Close();
}
}
catch(...){
result = false;
ShowMessage("Error");
}
delete Zip;
Zip = NULL;
return result;
}
void __fastcall OnZipProgressEvent( TObject* Sender, String FileName, TZipHeader Header, __int64 Position )
{
if ( PreviousFilename != FileName ){
PreviousFilename = FileName;
Form1->ProgressBar1->Max = Header.UncompressedSize;
Form1->ProgressBar1->Position = Position;
}
else{
Form1->ProgressBar1->Position = Position;
Application->ProcessMessages();
}
}
.h
#include <System.Zip.hpp>
public: // User declarations
__fastcall TForm1(TComponent* Owner);
typedef void __fastcall ( __closure * TZipProgressEvent )( TObject*,
String,
TZipHeader,
__int64 );
bool __fastcall createZipFromFolder( String inZipName,
String inSourcePath,
String inTargetPath );
void __fastcall OnZipProgressEvent( TObject* Sender,
String FileName,
TZipHeader Header,
__int64 Position );
};
extern PACKAGE TForm1 *Form1;
extern String PreviousFilename;
#endif
When calling Zip->ZipDirectoryContents(), you need to change OnZipProgressEvent() to &OnZipProgressEvent instead. You are trying to call the callback (with missing parameters) when you should be passing its address.
Also, in your .cpp file, both createZipFromFolder() and OnZipProgressEvent() need to be defined as being members of your Form class.
Also, in OnZipProgressEvent(), the TZipHeader parameter needs to be passed by const reference rather than by value, per the documentation.
Try this:
Unit1.h
...
class TForm1 : public TForm
{
...
private: // User declarations
String PreviousFilename;
void __fastcall OnZipProgressEvent( TObject* Sender,
String FileName,
const TZipHeader &Header,
__int64 Position );
public: // User declarations
__fastcall TForm1(TComponent* Owner);
bool __fastcall createZipFromFolder( String inZipName,
String inSourcePath,
String inTargetPath );
};
extern PACKAGE TForm1 *Form1;
#endif
Unit1.cpp
...
#include "Unit1.h"
#include <System.Zip.hpp>
bool __fastcall TForm1::createZipFromFolder( String inZipName,
String inSourcePath,
String inTargetPath )
{
bool result = true;
sZipName = _D("C:\\test\\testBar.zip");
sSourceName = _D("C:\\test2");
PreviousFilename = _D("");
TZipFile* Zip = new TZipFile();
try{
if ( FileExists( sZipName ) ){
Zip->ZipDirectoryContents( sZipName, // destiny
sSourceName, // to include
zcDeflate, // compression mode
&OnZipProgressEvent ); // <--- p callback
Zip->Close();
}
}
catch(...){
result = false;
ShowMessage(_D("Error"));
}
delete Zip;
Zip = NULL;
return result;
}
void __fastcall TForm1::OnZipProgressEvent( TObject* Sender,
String FileName,
const TZipHeader &Header,
__int64 Position )
{
if ( PreviousFilename != FileName ){
PreviousFilename = FileName;
ProgressBar1->Max = Header.UncompressedSize;
ProgressBar1->Position = Position;
}
else{
ProgressBar1->Position = Position;
Application->ProcessMessages();
}
}
Related
#include <iostream>
class BST {
private:
struct Bst_node
{
int data;
struct Bst_node* left;
struct Bst_node* right;
}*root;
// static Bst_node* get_inorder_predecessor(Bst_node* ptr);
static BST::Bst_node* get_inorder_predecessor(Bst_node* ptr)
{
ptr = ptr->left;
if (ptr == NULL) return NULL;
while (ptr->right != NULL)
{
ptr = ptr->right;
}
return ptr;
}
public:
void create()
{
root = new Bst_node;
root->data = 8;
root->right = NULL;
root->left = new Bst_node;
root->left->data = 10;
root->left->left = NULL;
root->left->right = NULL;
}
void print()
{
Bst_node * ptr = get_inorder_predecessor(this->root);
if(ptr == NULL) std::cout<<"NULL\n";
else std::cout<<ptr->data<<std::endl;
}
};
int main()
{
BST obj;
obj.create();
obj.print();
return 0;
}
The above code is working fine but when i define 'get_inorder_predecessor()' function outside the class it gives error see the below code.
#include <iostream>
class BST {
private:
struct Bst_node
{
int data;
struct Bst_node* left;
struct Bst_node* right;
}*root;
static Bst_node* get_inorder_predecessor(Bst_node* ptr);
public:
void create()
{`
root = new Bst_node;
root->data = 8;
root->right = NULL;
root->left = new Bst_node;
root->left->data = 10;
root->left->left = NULL;
root->left->right = NULL;
}
void print()
{
Bst_node * ptr = get_inorder_predecessor(this->root);
if(ptr == NULL) std::cout<<"NULL\n";
else std::cout<<ptr->data<<std::endl;
}
};
static BST::Bst_node* get_inorder_predecessor(Bst_node* ptr)
{
ptr = ptr->left;
if (ptr == NULL) return NULL;
while (ptr->right != NULL)
{
ptr = ptr->right;
}
return ptr;
}
int main()
{
BST obj;
obj.create();
obj.print();
return 0;
}
See the ERRORS:
'''binary.cpp:33:13: error: 'struct BST::Bst_node' is private within this context
static BST::Bst_node* get_inorder_predecessor(Bst_node* ptr)
^~~~~~~~
binary.cpp:4:12: note: declared private here
struct Bst_node
^~~~~~~~
binary.cpp:33:47: error: 'Bst_node' was not declared in this scope
static BST::Bst_node* get_inorder_predecessor(Bst_node* ptr)
^~~~~~~~
binary.cpp:33:57: error: 'ptr' was not declared in this scope
static BST::Bst_node* get_inorder_predecessor(Bst_node* ptr)
^~~
'''
I'm using RAD Studio 10.2 with Indy 10, a component IdUDPServer.
To test the speed of UDP connection, I installed the program UDP Test Tool.
My code for checking speed of the UDP server:
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
int readCounter = 0;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Clear();
TIdSocketHandle *SocketHandle = IdUDPServer1->Bindings->Add();
SocketHandle->IP = "127.0.0.1";
SocketHandle->Port = 14014;
IdUDPServer1->Active = true;
if (IdUDPServer1->Active == true) {
Memo1->Lines->Add("Сервер стартовал");
Button1->Enabled = false;
Button2->Enabled = true;
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Memo1->Clear();
IdUDPServer1->Active = false;
IdUDPServer1->Bindings->Clear();
if(IdUDPServer1->Active == false) {
Memo1->Lines->Add("Сервер остановлен");
Button1->Enabled = true;
Button2->Enabled = false;
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::IdUDPServer1UDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData,
TIdSocketHandle *ABinding)
{
readCounter++;
Edit1->Text = readCounter;
char* szBuff = new char[AData.Length];
memset(szBuff, 0, AData.Length);
BytesToRaw(AData, szBuff, AData.Length);
Memo1->Lines->Add(ToHex(AData));
Memo1->Clear();
delete szBuff;
}
//---------------------------------------------------------------------------
I use the readCounter in the method IdUDPServer1UDPRead for later comparison with the number of packets sent via the downloaded test utility, but the counters do not match. It can be seen that the component IdUDPServer is very late.
What could be the problem?
The TIdUDPServer::ThreadedEvent property is false by default. That means every time the server receives a data packet, your OnUDPRead event handler is synced with the main UI thread synchronously via TThread::Synchronize(). That will, of course, slow down the server's overall performance.
To make your server react to the UDP packets more quickly, you need to make your OnUDPRead event handler return control to TIdUDPServer more quickly. That means setting ThreadedEvent to true to skip the TThread::Synchronize() call, do not access the UI at all (or, at least, update the UI asynchronously using TThread::Queue() or TIdNotify), and remove any useless code that is slowing down your handler (you are allocating and filling a char[] buffer that is not being used for anything at all, so that is just wasted overhead).
For example, if you are using a Clang-based compiler:
// make sure ThreadedEvent=true...
void __fastcall TForm1::IdUDPServer1UDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData, TIdSocketHandle *ABinding)
{
++readCounter;
TThread::Queue(nullptr,
[=]() {
Edit1->Text = readCounter;
Memo1->Lines->Add(ToHex(AData));
}
);
}
Otherwise, if you are using a classic compiler:
// See "Handling Anonymous Method Types in C++":
// http://docwiki.embarcadero.com/RADStudio/en/How_to_Handle_Delphi_Anonymous_Methods_in_C%2B%2B
class TMyQueueProc : public TCppInterfacedObject<TThreadProcedure>
{
private:
int m_counter;
TIdBytes m_bytes;
public:
TMyQueueProc(int ACounter, const TIdBytes &AData) : m_counter(ACounter), m_bytes(AData) {}
INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);
void __fastcall Invoke()
{
Form1->Edit1->Text = m_counter;
Form1->Memo1->Lines->Add(ToHex(m_bytes));
}
};
// make sure ThreadedEvent=true...
void __fastcall TForm1::IdUDPServer1UDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData, TIdSocketHandle *ABinding)
{
++readCounter;
TThread::Queue(NULL, _di_TThreadProcedure(new TMyQueueProc(readCounter, AData)));
}
Or:
#include <IdSync.hpp>
class TMyNotify : public TIdNotify
{
private:
int m_counter;
TIdBytes m_bytes;
protected:
void __fastcall DoNotify()
{
Form1->Edit1->Text = m_counter;
Form1->Memo1->Lines->Add(ToHex(m_bytes));
}
public:
TMyNotify(int ACounter, const TIdBytes &AData) : TIdNotify(), m_counter(ACounter), m_bytes(AData) {}
};
// make sure ThreadedEvent=true...
void __fastcall TForm1::IdUDPServer1UDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData, TIdSocketHandle *ABinding)
{
++readCounter;
(new TMyNotify(readCounter, AData))->Notify();
}
I am using C++ Builder 10.2.2 Tokyo with FireMonkey (FMX).
I want to add drag and drop functionality to a TTreeView, so a user can rearrange the order of the tree items. I have added a handler to the TTreeView.OnMouseDown event, based on this Drag and Drop sample project.
With this, the program can now drag and drop to rearrange items, but it seems that there is some default behavior to move a TTreeViewItem to be a child of the TTreeViewItem it is dropped onto, instead of inserting after that item.
How can I override this default behavior, so that a TTreeViewItem is inserted at the same level in the TTreeView, and at an index 1 greater than the TTreeViewItem it is dropped onto?
Following on Abdullah's suggestion, you can achieve this by creating a custom component. Directions to create a custome component in general are here. I recommend installing it in Standard on the Tool Palette, as that's where TTreeView is.
The custom component, here called TMyTreeView, has this in the header in particular:
class PACKAGE TMyTreeView : public TTreeView
{
private:
bool IsAncestor (TTreeViewItem* oItem, TTreeViewItem* oTargetItem);
protected:
int DragDelta;
void StartDrag ();
void __fastcall DragDrop (const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point);
void __fastcall MouseDown (System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, float X, float Y);
void __fastcall MouseMove (System::Classes::TShiftState Shift, float X, float Y);
public:
__fastcall TMyTreeView(TComponent* Owner);
__fastcall ~TMyTreeView ();
TBitmap* DragBmp;
TPointF MouseDownPoint;
TTreeViewItem* DragStartItem;
__published:
};
//---------------------------------------------------------------------------
where the functions are as follows in the corresponding cpp file:
__fastcall TMyTreeView::TMyTreeView(TComponent* Owner)
: TTreeView(Owner)
{
DragBmp = NULL;
DragStartItem = NULL;
DragDelta = 5;
}
//---------------------------------------------------------------------------
__fastcall TMyTreeView::~TMyTreeView ()
{
if (DragBmp)
delete DragBmp;
}
//---------------------------------------------------------------------------
void __fastcall TMyTreeView::MouseMove (System::Classes::TShiftState Shift, float X, float Y)
{
TTreeView::MouseMove (Shift, X, Y);
if ((abs (X-MouseDownPoint.X) > DragDelta) || (abs (Y-MouseDownPoint.Y) > DragDelta))
StartDrag ();
}
//---------------------------------------------------------------------------
void TMyTreeView::StartDrag ()
{
if (!AllowDrag)
return;
if (!DragStartItem)
return;
if (DragBmp)
delete DragBmp;
_di_IFMXDragDropService service;
if ((TPlatformServices::Current->SupportsPlatformService (__uuidof(IFMXDragDropService)) &&
(service = TPlatformServices::Current->GetPlatformService (__uuidof(IFMXDragDropService)))))
{
TDragObject dragData;
if (!DragStartItem)
return;
dragData.Source = DragStartItem;
DragBmp = DragStartItem->MakeScreenshot ();
dragData.Data = DragBmp;
service->BeginDragDrop ((TForm*)this->Owner, dragData, DragBmp);
DragStartItem = NULL;
}
}
//---------------------------------------------------------------------------
void __fastcall TMyTreeView::MouseDown (System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, float X, float Y)
{
TTreeView::MouseDown (Button, Shift, X, Y);
if (AllowDrag)
{
DragStartItem = ItemByPoint (X, Y);
MouseDownPoint.X = X;
MouseDownPoint.Y = Y;
}
else
DragStartItem = NULL;
}
//---------------------------------------------------------------------------
void __fastcall TMyTreeView::DragDrop (const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point)
{
TTreeViewItem* item = ItemByPoint (Point.X, Point.Y);
if (!item)
return;
TTreeViewItem* srcItem = (TTreeViewItem*)Data.Source;
if (!srcItem)
return;
if (IsAncestor (srcItem, item))
return;
if (item->ParentItem ())
item->ParentItem ()->InsertObject (item->Index, srcItem);
else
this->InsertObject (item->Index, srcItem);
//TTreeView::DragDrop (Data, Point);
}
//---------------------------------------------------------------------------
bool TMyTreeView::IsAncestor (TTreeViewItem* oItem, TTreeViewItem* oTargetItem)
{
for (int i=0; i<oItem->Count; i++)
{
TTreeViewItem* item = oItem->Items [i];
if (item == oTargetItem)
return true;
if (IsAncestor (item, oTargetItem))
return true;
}
return false;
}
//---------------------------------------------------------------------------
After installing the custom component to your Tool Palette, you can then add it to your form as you would any other component.
Special thanks to Mike Sutton, who had code to modify an earlier version of TTreeView here.
Once added to a form, set the TMyTreeView control's AllowDrag to true.
My project is really acting up lately - I ironed out a whole bunch of errors, and then it throws a cryptic ld: symbol(s) not found error. However, it's clear that the symbols do exist:
Undefined symbols:
"Solid::~Solid()", referenced from:
void std::_Destroy<Solid>(Solid*)in ui.o
"Log::operator+=(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)", referenced from:
runUI() in ui.o
"Log::getLog(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)", referenced from:
runUI() in ui.o
"Log::Log()", referenced from:
__static_initialization_and_destruction_0(int, int)in ui.o
"Building::Building(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)", referenced from:
runUI() in ui.o
"Log::~Log()", referenced from:
___tcf_1 in ui.o
Here are the files it references:
log.cpp
/*
* log.cpp
*
* Created on: Apr 30, 2011
* Author: wjc
*/
#include <string>
#include <vector>
#include <sstream> // for converting count to string
#include "consts.h"
using namespace std;
struct logResult {
vector <string> result;
int status;
};
struct parsedLog {
string result;
int status;
};
class Log {
private:
bool initialized;
vector <string> * actionLog;
static int count;
string countString(){
stringstream ss;
ss<<count;
return ss.str();
}
public:
int initialize(){
if (initialized) return ALREADY_INIT;
actionLog->push_back("*** Log initialized ***");
actionLog->push_back("This is log#" + countString() +". If this is greater than one, there is a problem.");
initialized = true;
return SUCCESS;
}
int initialize(string text){
int initResult = initialize();
if (initResult == ALREADY_INIT) return ALREADY_INIT;
actionLog->push_back("Initialization message: "+text);
return SUCCESS;
}
Log (){
initialize();
count++;
}
Log (string text){
initialize(text);
count++;
}
~Log (){
count--;
}
bool isInitialized(){
return initialized;
}
int add(string text){
if (!initialized) return NOT_INIT;
actionLog->push_back(text);
return SUCCESS;
}
int operator+= (string text){
return add(text);
}
int clearLog(bool init = true){
if (!initialized) return NOT_INIT;
delete actionLog;
int initResult = SUCCESS;
if (init) initResult = initialize();
if (initResult == ALREADY_INIT) return ALREADY_INIT;
// Otherwise
// (no other possibilities because initialize()
// only returns either a SUCCESS or
// ALREADY_INIT value)
return SUCCESS;
}
logResult getLog(){
if (!initialized){
logResult final;
final.status = NOT_INIT;
return final;
} else {
logResult final;
final.result = *actionLog;
final.status = SUCCESS;
return final;
}
}
parsedLog getLog(string delim){
if (!initialized){
parsedLog final;
final.status = NOT_INIT;
return final;
} else {
parsedLog final;
string logString;
for (unsigned int i; i<actionLog->size()-1; i++){
logString += (*actionLog)[i];
logString += delim;
}
logString += actionLog->back();
final.result = logString;
final.status = SUCCESS;
return final;
}
}
};
log.h
/*
* log.h
*
* Created on: Apr 30, 2011
* Author: wjc
*/
#ifndef LOG_H_
#define LOG_H_
#include <string>
using namespace std;
struct logResult {
vector <string> result;
int status;
};
struct parsedLog {
string result;
int status;
};
class Log {
public:
int initialize();
int initialize(string text);
Log ();
Log (string text);
~Log();
bool isInitialized;
int add(string text);
int operator+= (string text);
int clearLog (bool init = true);
vector <string> getLog();
parsedLog getLog(string delim);
private:
bool initialized;
vector <string> actionLog;
static int count;
string countString();
};
#endif /* LOG_H_ */
ui.cppp
/*
* Created on: Apr 26, 2011
* ui.cpp
* Author: wjc
*/
#include <iostream>
#include <vector>
#include <sstream>
#include "filedaemon.h"
#include "vsystem.h"
#include "customio.h"
#include "building.h"
#include "log.h"
using namespace std;
bool shouldexit = false;
bool back = false;
int selection;
void addShape();
void modifyVars();
struct getVarResult {
var result;
int status;
};
Log actionLog;
var novar = {"ERROR", -1, Reserved};
getVarResult getVar(int type);
void viewBuilding();
int runUI(){
while (!shouldexit){
cout<<"Please select an item from the list below and press Enter:"<<endl;
const int mmenuLength = 2;
string mmenuOptions[2] = {"Create a new document","Quit"};
for (int i=0; i<2; i++){
cout<<i+1<<": "<<mmenuOptions[i]<<endl;
} cout<<endl;
selection = getMenuItem(1,mmenuLength);
if (selection == mmenuLength) return 0; // Quit if it's the last one
cout<<"Enter a name for your building:"<<endl;
string buildingTitle;
getline(cin, buildingTitle);
Building b(buildingTitle);
actionLog += "New building " + buildingTitle + " created.";
const int bmenuLength = 5;
string bmenuOptions[5] = {"Add shape","Modify variables","View building","View log","Quit"};
for (int i=0; i<bmenuLength; i++){
cout<<i+1<<": "<<bmenuOptions[i]<<endl;
} cout<<endl;
selection = getMenuItem(1,bmenuLength);
switch (selection){
case 1:
// Add a shape
break;
case 2:
modifyVars();
break;
case 3:
// View building
break;
case 4:
{
parsedLog parsed = actionLog.getLog("\n");
if (parsed.status == SUCCESS) {
cout<<"The following contains the contents of your action log."<<endl;
cout<<parsed.result<<endl<<endl;
} else {
cout<<"Somehow your log is not initialized."<<endl<<endl;
}
}
break;
case 5:
shouldexit = true;
break;
default:
cout<<"You entered a number greater than "<<bmenuLength<<" or less than 1. How you did this is a mystery."<<endl<<"[ Press Enter to exit ]"<<endl;
string temp;
getline(cin, temp);
return 0;
}
// The following commented-out block is a
// test of the variable storing system.
// It will not be used in any final products.
/*cout << " Variable Systems Test "<<endl;
cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~"<<endl;
cout << endl;
cout<<"Enter a variable name:"<<endl;
string varname;
cin>>varname;
cout<<"Enter a value (A FLOAT!):"<<endl;
float value;
cin>>value;
cout<<"Checking to see if "<<varname<<" exists."<<endl;
bool alreadythere = checkVar(varname);
alreadythere ? cout<<"It exists!"<<endl : cout<<"It doesn't exist."<<endl;
if (alreadythere){
cout<<"Changing variable. Function returned "<<changeVar(varname, value)<<endl;
} else {
cout<<"Setting variable. Function returned "<<addVar(varname, value)<<endl;
}
cout<<"Enter a variable to check:"<<endl;
string varcheck;
cin>>varcheck;
fetchResult result = fetchVar(varcheck);
if(! result.good){
cout<<"Variable \""<<varcheck<<"\" doesn't exist!"<<endl;
} else {
cout<<"Variable \""<<varcheck<<"\" is equal to "<<result.result<<endl;
}
cout<<getVarList("\n","\t")<<endl;
string exitstr;
getch;*/
}
return 0;
}
void modifyVars(){
while (! back){
cout<<"These are your defined variables."<<endl;
cout<<"Reserved variables have an asterisk preceding them."<<endl;
vector <var> vars = getVarList();
for (unsigned int i = 0; i<vars.size(); i++){
cout<<endl;
vars[i].reserved ? cout<<" * " : cout<<" ";
cout << vars[i].name<<" = ";
cout<<fixed<<vars[i].value;
}cout<<endl;
cout<<"What would you like to do?"<<endl;
string varMenuOptions[4] = {"Add a variable","Change a variable","Remove a variable","Go back"};
for (int i = 0; i<4; i++){
cout<<i+1<<". "<<varMenuOptions[i]<<endl;
} cout<<endl;
selection = getMenuItem(1,3);
switch(selection){
case 1: // Add variable
{
getVarResult gvr = getVar(ADD);
if (gvr.status == SUCCESS)
addVar(gvr.result.name, gvr.result.value, UserDefined);
break;
}
case 2: // Change variable
{
getVarResult gvr = getVar(CHANGE);
if (gvr.status == SUCCESS)
changeVar(gvr.result.name, gvr.result.value);
break;
} // switch (selection)
} // while (!back)
}
}
getVarResult getVar(int type){
getVarResult finalResult;
getVarResult invalidType;
getVarResult cancelled;
invalidType.result = novar;
invalidType.status = INVALID_TYPE;
cancelled.result = novar;
cancelled.status = USER_CANCELLED;
if (type != ADD && type != CHANGE) return invalidType;
bool usercancelled = false;
bool nameOK = true;
bool varIsReserved = false;
string varName;
do {
switch(type){
case ADD:
if (!nameOK) cout<<"That variable already exists."<<endl;
break;
case CHANGE:
if (!nameOK) cout<<"That variable has not yet been created."<<endl;
if (varIsReserved) cout<<"That variable is used by the system and cannot be changed."<<endl;
break;
}
cout<<"Enter the variable's name, or \"BACK\": "; varName = getString(1,16);
if (varName == "BACK"){
usercancelled = true;
break;
}
fetchResult testExist = fetchVar(varName);
switch(type){
case ADD:
nameOK = !testExist.good;
break;
case CHANGE:
nameOK = testExist.good;
varIsReserved = testExist.reserved;
break;
default:
cout << "Function error - int type seems to have changed since user called getVar(int type)."<<endl;
cout << "[ Press Enter to exit]"<<endl;
string temp;
getline(cin, temp);
return invalidType;
}
} while (! nameOK || varIsReserved);
finalResult.result.name = varName;
if (usercancelled) return cancelled;
bool valueOK = true;
float numValue;
do {
if (! valueOK) cout<<"That doesn't seem to be a valid positive number.";
cout<<"Enter the new value, or \"COPY\" to copy a variable, or \"BACK\":"<<endl;
string value = getString();
/*
* If "BACK" then break do-while(! valueOK)
*/
if (value == "BACK"){
usercancelled = true;
break;
}
if(value == "COPY"){
string copyVar;
fetchResult varContents;
bool copyOK = true;
do {
if (!copyOK) cout<<"That variable does not exist. Note that names are case-sensitive."<<endl;
cout<<"Enter the variable to copy, \"VIEW\" to view all, or \"BACK\":"<<endl;
/*
* If "BACK" then break do-while(! valueOK)
*/
if (value == "BACK"){
usercancelled = true;
break;
}
copyVar = getString(1,8);
if (copyVar == "VIEW") {
cout<<"Your current variables are as follows:"<<endl;
vector <var> vars = getVarList();
for (unsigned int i = 0; i<vars.size(); i++){
cout<<endl;
vars[i].reserved ? cout<<" * " : cout<<" ";
cout << vars[i].name<<" = ";
cout<<fixed<<vars[i].value;
}cout<<endl;
} else {
varContents = fetchVar(copyVar);
copyOK = varContents.good;
numValue = varContents.result;
}
} while (copyVar == "VIEW" || ! copyOK);
} else {
// This code converts from string to number safely.
stringstream testStream(value);
if (! (testStream >> numValue))
valueOK = false;
}
if (! usercancelled) break;
} while (! valueOK);
finalResult.result.value = numValue;
if (usercancelled) return cancelled;
finalResult.status = SUCCESS;
return finalResult;
}
ui.h
/*
* ui.h
*
* Created on: Apr 26, 2011
* Author: wjc
*/
#ifndef UI_H_
#define UI_H_
#include "vsystem.h"
int runUI();
void addShape();
void modifyVars();
struct getVarResult {
var result;
int status;
};
getVarResult getVar(int type);
void viewBuilding();
#endif /* UI_H_ */
building.h
/*
* building.h
*
* Created on: Apr 30, 2011
* Author: wjc
*/
#ifndef BUILDING_H_
#define BUILDING_H_
#include <string>
#include <vector>
#include "consts.h"
#include "solid_type.h"
using namespace std;
struct dimension {
bool exists;
float value;
};
class Solid {
public:
string name;
string comment;
solid_type type;
bool positive;
dimension dim1; // Radius, width, or side length
dimension dim2; // Height, number of sides, or fraction of sphere_over_n
dimension dim3; // Width - only for prism_rect, pyrm_rect and tprym_rect
Solid ();
Solid (bool pos);
Solid (string setName, string setComment, solid_type setType, bool setPos, dimension setDim1,
dimension setDim2, dimension setDim3);
~Solid();
int countShapes();
int howMany();
private:
static int count;
};
class Building {
private:
string name;
vector <Solid> components;
public:
Building(string text);
void setName(string text);
string getName();
vector <Solid> addComponent(Solid component);
};
#endif /* BUILDING_H_ */
building.cpp
/*
* building.cpp
*
* Created on: May 1, 2011
* Author: wjc
*/
#include <string>
#include <vector>
#include "consts.h"
#include "solid_type.h"
using namespace std;
struct dimension {
bool exists;
float value;
};
class Solid{
public:
string name; // So the user can look at the log
string comment; // Expanded version of name, again for the log
solid_type type; // Determines the type of Solid
bool positive; // Positive = addition; negative = subtraction
dimension dim1; // Radius, width, or side length
dimension dim2; // Height, number of sides, or fraction of sphere_over_n
dimension dim3; // Width - only for prism_rect, pyrm_rect and tprym_rect
Solid(){
count++;
}
Solid(bool setPos){
count++;
positive = setPos;
}
Solid (string setName, string setComment, solid_type setType, bool setPos, dimension setDim1,
dimension setDim2, dimension setDim3){
count++;
name = setName;
comment = setComment;
type = setType;
positive = setPos;
dim1 = setDim1;
dim2 = setDim2;
dim3 = setDim3;
}
~Solid(){
count--;
}
int howMany(){
return count;
}
int countSolids(){
return howMany();
}
private:
static int count; // Number of Solids in existence
};
class Building {
private:
string name;
vector <Solid> components;
public:
Building (string text){
setName(text);
}
void setName(string text) {
name = text;
}
string getName(){
return name;
}
vector <Solid> addComponent(Solid component){
components.push_back(component);
return components;
}
};
My complete code is available at FileDropper, although I know most people won't want to unpack a zip, so I put the relevant ones above.
Any help would be greatly appreciated!
Running Eclipse Helios with G++ 4.2.1 on Mac OS X Snow Leopard 10.6.7.
The complete linking command is:
g++ -o "BuildingGenerator" ./src/bgmath.o ./src/building.o ./src/customio.o \
./src/filedaemon.o ./src/log.o ./src/main.o ./src/ui.o ./src/vsystem.o
So it looks like it is linking all the necessary files. Also, the (header) files are all #include'd where needed.
Problem discovered; outline fix created...
Code received, unpacked, and compiled; the linker produces essentially the same error that you're seeing...
Osiris-8 JL: make bgmath.o building.o customio.o filedaemon.o log.o main.o ui.o vsystem.o
g++ -c -o bgmath.o bgmath.cpp
g++ -c -o building.o building.cpp
g++ -c -o customio.o customio.cpp
g++ -c -o filedaemon.o filedaemon.cpp
g++ -c -o log.o log.cpp
g++ -c -o main.o main.cpp
g++ -c -o ui.o ui.cpp
g++ -c -o vsystem.o vsystem.cpp
Osiris-8 JL: g++ -o cmd *.o
Undefined symbols for architecture x86_64:
"Building::Building(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)", referenced from:
runUI() in ui.o
"Log::operator+=(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)", referenced from:
runUI() in ui.o
"Log::getLog(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)", referenced from:
runUI() in ui.o
"Log::Log()", referenced from:
__static_initialization_and_destruction_0(int, int) in ui.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
Osiris-8 JL:
This is on MacOS X 10.6.7 with GCC 4.6.0 (which I compiled). Since it is substantially the same as what you got, the version of G++ is essentially irrelevant (but not wholly; the c++filt distributed along with GCC 4.2.1 by Apple does not recognize the 'nm' output you provided, even after cleaning the BOM out of the file).
OK - several grey hairs later - I know roughly what is going on.
When I run nm log.o, it said 'nm: no name list'. That's because it is, in fact, an empty object file. And it is an empty object file because all the code is in a class declaration, but is never used so there is no object code in the file, and no functions.
I had a suspicion that it was something to do with definitions - I had not guessed it was as completely 'no defined functions' as it is.
So, how do we fix it?
The first problem is that you have log.cpp but it does not include log.h. This is a strong indicator of problems. The header defines the public interface to the class(es) defined in the source, and the only (sane) way of ensuring they are in agreement is to include the header in the source file. In fact, it is best to include the header first so that you can be sure it is self-contained (can be used in any source module).
When we do that, we find immediately that log.h is not self-contained -- it needs #include <vector> too. When that's fixed, we find that you cannot compile log.cpp successfully:
g++ -c -o log.o log.cpp
log.cpp:12:8: error: redefinition of ‘struct logResult’
log.h:15:8: error: previous definition of ‘struct logResult’
log.cpp:17:8: error: redefinition of ‘struct parsedLog’
log.h:20:8: error: previous definition of ‘struct parsedLog’
log.cpp:22:7: error: redefinition of ‘class Log’
log.h:25:7: error: previous definition of ‘class Log’
make: *** [log.o] Error 1
The two structures are declared in the header, so they should not be redeclared in the source. The function definitions must be prefixed with Log:: and removed from the 'class Log { ... }' braces. There is then routine clean up of the compilation errors, leading to:
log.h
#ifndef LOG_H_
#define LOG_H_
#include <vector>
#include <string>
using namespace std;
struct logResult {
vector <string> result;
int status;
};
struct parsedLog {
string result;
int status;
};
class Log {
public:
int initialize();
int initialize(string text);
Log ();
Log (string text);
bool isInitialized();
int add(string text);
int operator+= (string text);
int clearLog (bool init = true);
logResult getLog();
parsedLog getLog(string delim);
private:
bool initialized;
vector <string> actionLog;
};
#endif /* LOG_H_ */
log.cpp
#include "log.h"
#include "consts.h"
using namespace std;
int Log::initialize(){
if (initialized) return ALREADY_INIT;
actionLog.push_back("*** Log initialized ***");
initialized = true;
return SUCCESS;
}
int Log::initialize(string text){
if (initialized) return ALREADY_INIT;
// otherwise...
initialize();
actionLog.push_back("Initialization message: "+text);
return SUCCESS;
}
Log::Log (){
initialize();
}
Log::Log (string text){
initialize(text);
}
bool Log::isInitialized(){
return initialized;
}
int Log::add(string text){
if (!initialized) return NOT_INIT;
actionLog.push_back(text);
return SUCCESS;
}
int Log::operator+= (string text){
return add(text);
}
int Log::clearLog(bool init){
if (!initialized) return NOT_INIT;
//delete actionLog;
initialized = false;
if (init) return initialize();
// Else
return SUCCESS;
}
logResult Log::getLog(){
if (!initialized){
logResult final;
final.status = NOT_INIT;
return final;
} else {
logResult final;
final.result = actionLog;
final.status = SUCCESS;
return final;
}
}
parsedLog Log::getLog(string delim){
if (!initialized){
parsedLog final;
final.status = NOT_INIT;
return final;
} else {
parsedLog final;
string logString;
for (unsigned int i; i<actionLog.size()-1; i++){
logString += actionLog[i];
logString += delim;
}
logString += actionLog.back();
final.result = logString;
final.status = SUCCESS;
return final;
}
}
These files compile cleanly to generate log.o containing some useful functions. The link failure after that is:
Undefined symbols for architecture x86_64:
"Building::Building(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)", referenced from:
runUI() in ui.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
Fixing that is left as an exercise for you - but I believe analogous changes are needed in building.h and building.cpp.
There are still some issues that should be resolved. In particular, headers should not usually do using namespace std; as that is an unwarranted intrusion into other programmer's control over the namespaces they use. See Using namespace in C++ headers for a recent discussion of this issue.
I've just started using Nant for my builds and tests. I want to make it change the color (or background) of my command prompt text when it fails so its easily noticed.
The command in command prompt on Windows is 'color 4' to change it to red and color 7 for back to white.
How do I make this run in a build script, echo doesn't work, exec doesn't work (may be using exec wrong though). I'd prefer to not have to run perl etc just to do something which is easily done in a standard command prompt window.
Does anyone know how to do this?
Try using a custom task. If the task is included in the nant-file you'll not have any external dependency.
<project >
<target name="color">
<consolecolor color="Red" backgroundcolor="White"/>
<echo message="red text"/>
<consolecolor color="White" backgroundcolor="Black"/>
<echo message="white text"/>
</target>
<script language="C#">
<code>
[TaskName("consolecolor")]
public class TestTask : Task
{
private string _color;
private string _backgroundColor;
[TaskAttribute("color",Required=true)]
public string Color
{
get { return _color; }
set { _color = value; }
}
[TaskAttribute("backgroundcolor",Required=false)]
public string BackgroundColor
{
get { return _backgroundColor; }
set { _backgroundColor = value; }
}
protected override void ExecuteTask()
{
System.Console.ForegroundColor = (System.ConsoleColor) Enum.Parse(typeof(System.ConsoleColor),Color);
System.Console.BackgroundColor = (System.ConsoleColor) Enum.Parse(typeof(System.ConsoleColor),BackgroundColor);
}
}
</code>
</script>
</project>
As a follow up to my comment on the post by #Martin-Vobr:
I have added additional logic to properly change the background. This will allow a build to be initiated in a command window, then progress can be checked at a glance. I use a blue background for "building", green for "Success" and red for "Failure".
<!-- http://stackoverflow.com/questions/3446135/how-to-run-color-command-in-nant-script -->
<!-- Sample: <consolecolor color="Red" backgroundcolor="White"/> -->
<!-- Alternative: http://riccardotramma.com/2011/05/nantcolours-v1-0-a-task-library-for-output-colouring-in-nant/ -->
<script language="C#">
<code>
<![CDATA[
[TaskName("consolecolor")]
public class TestTask : Task
{
private string _color;
private string _backgroundColor;
[TaskAttribute("color",Required=true)]
public string Color
{
get { return _color; }
set { _color = value; }
}
[TaskAttribute("backgroundcolor",Required=false)]
public string BackgroundColor
{
get { return _backgroundColor; }
set { _backgroundColor = value; }
}
protected override void ExecuteTask()
{
System.Console.ForegroundColor = (System.ConsoleColor) Enum.Parse(typeof(System.ConsoleColor),Color);
System.Console.BackgroundColor = (System.ConsoleColor) Enum.Parse(typeof(System.ConsoleColor),BackgroundColor);
// clearing the screen sets the entire screen to be the new color
ChangeColor((System.ConsoleColor) Enum.Parse(typeof(System.ConsoleColor),Color), (System.ConsoleColor) Enum.Parse(typeof(System.ConsoleColor),BackgroundColor));
}
// added by Brad Bruce
// http://stackoverflow.com/questions/6460932/change-entire-console-background-color-win32-c
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
static extern bool ReadConsoleOutputAttribute(IntPtr hConsoleOutput,
[System.Runtime.InteropServices.Out] ushort[] lpAttribute, uint nLength, COORD dwReadCoord,
out uint lpNumberOfAttrsRead);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
static extern bool FillConsoleOutputAttribute(IntPtr hConsoleOutput,
ushort wAttribute, uint nLength, COORD dwWriteCoord, out uint
lpNumberOfAttrsWritten);
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct COORD {
public short X;
public short Y;
public COORD(short X, short Y) {
this.X = X;
this.Y = Y;
}
};
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
//C#: Get stdout handle
const int STD_OUTPUT_HANDLE = -11;
const int STD_INPUT_HANDLE = -10;
const int STD_ERROR_HANDLE = -12;
//INVALID_HANDLE_VALUE //(return value if invalid handle is specified)
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes
public enum CharacterAttributes{
FOREGROUND_BLUE = 0x0001,
FOREGROUND_GREEN = 0x0002,
FOREGROUND_RED = 0x0004,
FOREGROUND_INTENSITY = 0x0008,
BACKGROUND_BLUE = 0x0010,
BACKGROUND_GREEN = 0x0020,
BACKGROUND_RED = 0x0040,
BACKGROUND_INTENSITY = 0x0080,
COMMON_LVB_LEADING_BYTE = 0x0100,
COMMON_LVB_TRAILING_BYTE = 0x0200,
COMMON_LVB_GRID_HORIZONTAL = 0x0400,
COMMON_LVB_GRID_LVERTICAL = 0x0800,
COMMON_LVB_GRID_RVERTICAL = 0x1000,
COMMON_LVB_REVERSE_VIDEO = 0x4000,
COMMON_LVB_UNDERSCORE = 0x8000
}
static void ChangeColor(System.ConsoleColor color, System.ConsoleColor backgroundColor) {
uint written = 0;
COORD writeCoord = new COORD(0, 0);
ushort[] attribute = new ushort[400];
IntPtr consoleOutputHandle = GetStdHandle( STD_OUTPUT_HANDLE );
int consoleBufferWidth = Console.BufferWidth;
int consoleBufferLength = Console.BufferHeight;
//if (consoleBufferLength > Console.CursorTop) {
// consoleBufferLength = Console.CursorTop;
//}
for (int y = 0; y < consoleBufferLength; y++) // rows
{
writeCoord.X = (short)0;
writeCoord.Y = (short)y;
ReadConsoleOutputAttribute(consoleOutputHandle, attribute, (uint)consoleBufferWidth, writeCoord, out written);
for (int x2 = 0; x2 < consoleBufferWidth; x2++){ // columns
attribute[x2] &= 0xFF00; // zero the background and foreground color
attribute[x2] |= (ushort)((((int)backgroundColor) << 4) | (int)color);
}
FillConsoleOutputAttribute(consoleOutputHandle, attribute[0], (uint)consoleBufferWidth, writeCoord, out written);
}
}
}
]]>
</code>
</script>