SQLite and UTF8 - qt

I have a strange problem. Cant add function to sqlite/ Write on c++/Qt.
Function should do upper to Utf-8 simbols;
this->db = QSqlDatabase::addDatabase("QSQLITE");
this->db.setDatabaseName(db);
this->db.open();
QVariant v = this->db.driver()->handle();
sqlite3 *handler = *static_cast<sqlite3 **>(v.data());
sqlite3_create_function( handler, "myUpper", 1, SQLITE_ANY, NULL, myFunc, NULL, NULL )
and the function is:
void myFunc( sqlite3_context * ctx, int argc, sqlite3_value ** argv )
{
if( argc != 1 ) return;
QString str;
switch(sqlite3_value_type(argv[0]))
{
case SQLITE_NULL:
{
sqlite3_result_text( ctx, "NULL", 4, SQLITE_STATIC );
break;
}
case SQLITE_TEXT:
{
QString wstr((char*)sqlite3_value_text16(argv[0]));
wstr = wstr.toUpper();
sqlite3_result_text16( ctx, wstr.toStdString().c_str(), wstr.size() , SQLITE_TRANSIENT );
break;
}
default:
sqlite3_result_text( ctx, "NULL", 4, SQLITE_STATIC );
break;
}
}

You didn't specify the problem, but I suppose you should pass a pointer to the function:
sqlite3_create_function(handler, "myUpper", 1, SQLITE_ANY, NULL, &myFunc, NULL, NULL)
EDIT:
I you're passing to the function wstr.toStdString().c_str(). This is not allowed because the pointer returned is only temporary and will go out of scope. What I would do is:
...
case SQLITE_TEXT: {
QString wstr((char*)sqlite3_value_text(argv[0]));
wstr = wstr.toUpper();
QByteArray array = wstr.toLocal8Bit();
const char* cstr = array.data();
sqlite3_result_text(ctx, cstr, wstr.size() , SQLITE_TRANSIENT);
break;
}
...
I didn't try it so check this.

Related

segfault when reading QTextStream

Context:
I have a set of file like that:
alias
USER = usr_name
PASSWORD = pass
...
alias2
...
I have a combobox to choose which file to display in a qtableview. The below is the slot called when the value of the combobox changes.
void paramTableModel::switchTable(QString env)
{
// is there an environment defined
// --------------------------------------
if(env.compare("") != 0)
{
// does the param file exist ?
// ---------------------------
QString file_path = "/path/to/" + env + "/path/to/file;
QFile param_file(file_path);
if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
{
QString program_decrypt = "/path/to/decrypt";
QStringList args;
args << file_path;
QProcess* process = new QProcess();
process->setReadChannel(QProcess::StandardOutput);
process->start(program_decrypt,args);
process->waitForFinished(-1);
QTextStream in(&process->readAll());
//start resetting model (notify the view )
beginResetModel();
this->table.clear();
std::set<QString> uniq;
// parse file to model
// -------------------
while( !in.atEnd() )
{
QString line = in.readLine();
// some new UT ?
// -------------------
if( !line.isNull() && line.trimmed().compare("") != 0 && !line.contains("="))
{
line = line.trimmed();
std::vector<QString> row;
if(uniq.find(line) == uniq.end())
{
uniq.insert(line);
row.push_back(line);
QString user_line = in.readLine().trimmed();
QString password_line = in.readLine().trimmed();
QString server_line = in.readLine().trimmed();
if( user_line.startsWith("USER") )
{
user_line = user_line.split('=').at(1).trimmed();
}
if( password_line.startsWith("PASSWORD") )
{
password_line = password_line.split('=').at(1).trimmed();
}
if( server_line.startsWith("SERVER") )
{
server_line = server_line.split('=').at(1).trimmed();
}
row.push_back(user_line);
row.push_back(password_line);
row.push_back(server_line);
this->table.push_back(row);
}
else
{
QMessageBox msgBox;
msgBox.setText(QString("%1 is already defined, ingnoring").arg(line));
msgBox.exec();
}
}
}
param_file.close();
endResetModel();
delete process;
}
}
}
Most of the time, my view is properly refreshed. And from time to time, this line segfaults:
QString line = in.readLine();
I have found no pattern to reproduce the bug in a systematic way. I can click back and forth between two files in the combobox, it will work ten, twelve, twenty times and then segfault.
EDIT:
This does work.
void paramTableModel::switchTable(QString env)
{
// is there an environment defined
// --------------------------------------
if(env.isEmpty())
return;
// does the param file exist ?
// --------------------------------
QString file_path = "/path/to/env/" + env + "/path/to/file.dat";
QFile param_file(file_path);
if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
{
// Call QProcess, fx_decrypt, read standard output
QString program_decrypt = "/path/to/decrypt";
QStringList args;
args << file_path;
QProcess process;
process.setReadChannel(QProcess::StandardOutput);
process.start(program_decrypt,args);
process.waitForFinished(-1);
//QTextStream in(&process->readAll());
QString out = process.readAll();
QStringList list_out = out.split("\n",QString::SkipEmptyParts);
//start resetting model (notify the view )
beginResetModel();
this->table.clear();
std::set<QString> uniq;
// parse file to model
// -----------------------
//while( !in.atEnd() )
int counter = 0;
while(counter < list_out.size())
{
//QString line = in.readLine();
QString line = list_out.at(counter);
// some new UT ?
// -------------------
if( !line.isNull() && !line.trimmed().isEmpty() != 0 && !line.contains("="))
{
line = line.trimmed();
std::vector<QString> row;
if(uniq.find(line) == uniq.end())
{
uniq.insert(line);
row.push_back(line);
/**QString user_line = in.readLine().trimmed();
QString password_line = in.readLine().trimmed();
QString server_line = in.readLine().trimmed();*/
QString user_line = list_out.at(++counter).trimmed();
QString password_line = list_out.at(++counter).trimmed();
QString server_line = list_out.at(++counter).trimmed();
if( user_line.startsWith("USER") )
{
user_line = user_line.split('=').at(1).trimmed();
}
if( password_line.startsWith("PASSWORD") )
{
password_line = password_line.split('=').at(1).trimmed();
}
if( server_line.startsWith("SERVER") )
{
server_line = server_line.split('=').at(1).trimmed();
}
row.push_back(user_line);
row.push_back(password_line);
row.push_back(server_line);
this->table.push_back(row);
}
else
{
QMessageBox msgBox;
msgBox.setText(QString("%1 is already defined, ingnoring").arg(line));
msgBox.exec();
}
}
++counter;
}
endResetModel();
}
}
Trying to get the why of the bug, following #Kuba Ober suggestion
I've tried to copy the first version into a main function looping indefinitely over all my environments:
But it does work fine!
#include <QtCore/QCoreApplication>
#include <QProcess>
#include <QTextStream>
#include <set>
#include <vector>
#include <QDebug>
#include <QFile>
#include <deque>
#include <QDir>
#include <QStringList>
std::deque< std::vector < QString > > table;
void f( QString file_path )
{
// does the param file exist ?
// --------------------------------
QFile param_file(file_path);
if( param_file.exists() && param_file.open(QIODevice::ReadOnly))
{
// Call QProcess, fx_decrypt, read standard output
QString program_decrypt = "/path/to/decrypt";
QStringList args;
args << file_path;
QProcess* process = new QProcess();
process->setReadChannel(QProcess::StandardOutput);
process->start(program_decrypt,args);
process->waitForFinished(-1);
QTextStream in(&process->readAll());
//start resetting model (notify the view )
table.clear();
std::set<QString> uniq;
// parse file to model
// -----------------------
while( !in.atEnd() )
{
QString line = in.readLine();
// some new UT ?
// -------------------
if( !line.isNull() && line.trimmed().isEmpty() != 0 && !line.contains("="))
{
line = line.trimmed();
std::vector<QString> row;
if(uniq.find(line) == uniq.end())
{
uniq.insert(line);
row.push_back(line);
QString user_line = in.readLine().trimmed();
QString password_line = in.readLine().trimmed();
QString server_line = in.readLine().trimmed();
if( user_line.startsWith("USER") )
{
user_line = user_line.split('=').at(1).trimmed();
}
if( password_line.startsWith("PASSWORD") )
{
password_line = password_line.split('=').at(1).trimmed();
}
if( server_line.startsWith("SERVER") )
{
server_line = server_line.split('=').at(1).trimmed();
}
row.push_back(user_line);
row.push_back(password_line);
row.push_back(server_line);
table.push_back(row);
}
else
{
qDebug() << QString("%1 is already defined, ingnoring").arg(line);
}
}
}
param_file.close();
delete process;
}
}
QStringList getEnvList()
{
QDir dir("/path/to/env/");
QStringList list_env;
foreach(QString folder, dir.entryList(QStringList(),QDir::AllDirs | QDir::NoDotAndDotDot))
{
QFile app_file( dir.absolutePath() + '/' + folder + '/' + "file.dat" );
if (app_file.exists())
{
list_env.push_back(folder);
}
}
return list_env;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int counter = 0;
while(true)
{
foreach ( QString s, getEnvList() )
{
qDebug() << counter++;
qDebug() << s;
f(s);
}
}
return a.exec();
}
I have no idea, why the two below codes never crash while the first one does...

using QString as argmuent in SLOT macro

Is it possible to be used as an argument QString in SLOT macro?
PS. I mean a simple solution .. Not as in QMetaObject::connectSlotsByName().
No you cannot pass QString to SLOT macro. But you can use QString for connect. Also connect cannot take QString, so you have to convert it to const char *. Simple example is:
QString slotName = SLOT(clicked());
connect(ui->pushButton, SIGNAL(clicked()), qPrintable(slotName));
SLOT just stringifies passed parameter and concatenate it with 1:
# define SLOT(a) qFlagLocation("1"#a QLOCATION)
If you don't want to use SLOT it's possible to write code like this:
QString slotName = QString::number(QSLOT_CODE) + "clicked()";
This is raw code from my current project. It parses chat commands like /me customtext and calls cmd_me( const QString& params ); slot.
To introduce new command it's enough to create private slot with void cmd_*( const QString& ); signature.
Here is code:
void ConsoleController::onCommand( const QString& cmd )
{
if ( cmd.length() < 1 )
return ;
if ( cmd[0] != '/' )
return ;
const QMetaObject *meta = metaObject();
int posParam = cmd.indexOf( ' ' );
if ( posParam == -1 )
posParam = cmd.length();
const QString command = cmd.mid( 1, posParam - 1 ).toLower();
const QString params = cmd.mid( posParam + 1 );
const QString slotName = QString( "cmd_%1( const QString& )" ).arg( command );
const QString normalizedSlotName = QMetaObject::normalizedSignature( slotName.toStdString().c_str() );
for ( int i = 0; i < meta->methodCount(); i++ )
{
QMetaMethod method = meta->method( i );
if ( method.methodType() != QMetaMethod::Slot )
continue;
const QString signature = method.signature();
if ( normalizedSlotName == signature )
{
method.invoke( this, Q_ARG( QString, params ) );
return ;
}
}
log( QString( "Command \"%1\" not recognized, type /help to list all available commands" ).arg( command ) );
}
You can take an idea and adapt it for your needs.

CutyCapt not able to generate HTTPS web site screenshot when launched from w3wp.exe

I have updated the latest code from SVN and complie CutyCapt with Qt SDK 4.8.
And I have static-linked libeay32.dll / ssleay32.dll in the same location of the binary.
It works fine if I launch the CutyCapt process in command window.
It works fine if I launch the CutyCapt process from ASP.NET (w3wp) to capture HTTP web page.
it does NOT work if I launch the CutyCapt process from ASP.NET (w3wp) to capture HTTPS web page, always generates a blank image.
I doubted it was because the libeay32.dll / ssleay32.dll dependencies are not loaded, so I copied these 2 dll to system32 / SysWOW64, this does not resolve my problem.
Then I monitor the process by ProcessMonitor, it shows me that libeay32.dll / ssleay32.dll are loaded successfully.
So, the dependencies are not the reason.
Here is my C# code to launch the CutyCapt,
using (Process process = new Process())
{
process.StartInfo.FileName = ti.ExeFile;
process.StartInfo.ErrorDialog = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(ti.ExeFile);
process.StartInfo.UseShellExecute = false;
process.StartInfo.Arguments = string.Format(#"--min-width=1024 --delay=1500 --javascript=off --auto-load-images=on --plugins=off --java=off --url={0} --out={1}"
, ti.Url
, destFile
);
process.Start();
process.WaitForExit();
process.Close();
}
Does anyone knows how to allow CutyCapt works from w3wp?
SOLUTION:
I have resolved this problem by modifying the CutyCapt source.
The idea is that, get the HTML of the url then set the HTML to the WebKit.
CutyCapt.hpp
#include <QtWebKit>
class CutyCapt;
class CutyPage : public QWebPage {
Q_OBJECT
public:
void setAttribute(QWebSettings::WebAttribute option, const QString& value);
void setUserAgent(const QString& userAgent);
void setAlertString(const QString& alertString);
void setPrintAlerts(bool printAlerts);
void setCutyCapt(CutyCapt* cutyCapt);
QString getAlertString();
protected:
QString chooseFile(QWebFrame *frame, const QString& suggestedFile);
void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID);
bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result);
void javaScriptAlert(QWebFrame* frame, const QString& msg);
bool javaScriptConfirm(QWebFrame* frame, const QString& msg);
QString userAgentForUrl(const QUrl& url) const;
QString mUserAgent;
QString mAlertString;
bool mPrintAlerts;
CutyCapt* mCutyCapt;
};
class CutyCapt : public QObject {
Q_OBJECT
public:
QString mUrl;
// TODO: This should really be elsewhere and be named differently
enum OutputFormat { SvgFormat, PdfFormat, PsFormat, InnerTextFormat, HtmlFormat,
RenderTreeFormat, PngFormat, JpegFormat, MngFormat, TiffFormat, GifFormat,
BmpFormat, PpmFormat, XbmFormat, XpmFormat, OtherFormat };
CutyCapt(CutyPage* page,
const QString& output,
int delay,
OutputFormat format,
const QString& scriptProp,
const QString& scriptCode);
private slots:
void HtmlDownloadFinished(QNetworkReply * reply);
void HtmlDownloadError(QNetworkReply::NetworkError code);
void DocumentComplete(bool ok);
void InitialLayoutCompleted();
void JavaScriptWindowObjectCleared();
void Timeout();
void Delayed();
private:
void TryDelayedRender();
void saveSnapshot();
bool mSawInitialLayout;
bool mSawDocumentComplete;
protected:
QString mOutput;
int mDelay;
CutyPage* mPage;
OutputFormat mFormat;
QObject* mScriptObj;
QString mScriptProp;
QString mScriptCode;
};
CutyCapt.cpp
////////////////////////////////////////////////////////////////////
//
// CutyCapt - A Qt WebKit Web Page Rendering Capture Utility
//
// Copyright (C) 2003-2010 Bjoern Hoehrmann <bjoern#hoehrmann.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// $Id: CutyCapt.cpp 7 2011-11-30 10:56:34Z hoehrmann $
//
////////////////////////////////////////////////////////////////////
#include <QApplication>
#include <QtWebKit>
#include <QtGui>
#include <QSvgGenerator>
#include <QPrinter>
#include <QTimer>
#include <QByteArray>
#include <QNetworkRequest>
#include <QtCore>
#include "CutyCapt.hpp"
#if QT_VERSION >= 0x040600 && 0
#define CUTYCAPT_SCRIPT 1
#endif
#ifdef STATIC_PLUGINS
Q_IMPORT_PLUGIN(qjpeg)
Q_IMPORT_PLUGIN(qgif)
Q_IMPORT_PLUGIN(qtiff)
Q_IMPORT_PLUGIN(qsvg)
Q_IMPORT_PLUGIN(qmng)
Q_IMPORT_PLUGIN(qico)
#endif
static struct _CutyExtMap {
CutyCapt::OutputFormat id;
const char* extension;
const char* identifier;
} const CutyExtMap[] = {
{ CutyCapt::SvgFormat, ".svg", "svg" },
{ CutyCapt::PdfFormat, ".pdf", "pdf" },
{ CutyCapt::PsFormat, ".ps", "ps" },
{ CutyCapt::InnerTextFormat, ".txt", "itext" },
{ CutyCapt::HtmlFormat, ".html", "html" },
{ CutyCapt::RenderTreeFormat, ".rtree", "rtree" },
{ CutyCapt::JpegFormat, ".jpeg", "jpeg" },
{ CutyCapt::PngFormat, ".png", "png" },
{ CutyCapt::MngFormat, ".mng", "mng" },
{ CutyCapt::TiffFormat, ".tiff", "tiff" },
{ CutyCapt::GifFormat, ".gif", "gif" },
{ CutyCapt::BmpFormat, ".bmp", "bmp" },
{ CutyCapt::PpmFormat, ".ppm", "ppm" },
{ CutyCapt::XbmFormat, ".xbm", "xbm" },
{ CutyCapt::XpmFormat, ".xpm", "xpm" },
{ CutyCapt::OtherFormat, "", "" }
};
QString
CutyPage::chooseFile(QWebFrame* /*frame*/, const QString& /*suggestedFile*/) {
return QString::null;
}
bool
CutyPage::javaScriptConfirm(QWebFrame* /*frame*/, const QString& /*msg*/) {
return true;
}
bool
CutyPage::javaScriptPrompt(QWebFrame* /*frame*/,
const QString& /*msg*/,
const QString& /*defaultValue*/,
QString* /*result*/) {
return true;
}
void
CutyPage::javaScriptConsoleMessage(const QString& /*message*/,
int /*lineNumber*/,
const QString& /*sourceID*/) {
// noop
}
void
CutyPage::javaScriptAlert(QWebFrame* /*frame*/, const QString& msg) {
if (mPrintAlerts)
qDebug() << "[alert]" << msg;
if (mAlertString == msg) {
QTimer::singleShot(10, mCutyCapt, SLOT(Delayed()));
}
}
QString
CutyPage::userAgentForUrl(const QUrl& url) const {
if (!mUserAgent.isNull())
return mUserAgent;
return QWebPage::userAgentForUrl(url);
}
void
CutyPage::setUserAgent(const QString& userAgent) {
mUserAgent = userAgent;
}
void
CutyPage::setAlertString(const QString& alertString) {
mAlertString = alertString;
}
QString
CutyPage::getAlertString() {
return mAlertString;
}
void
CutyPage::setCutyCapt(CutyCapt* cutyCapt) {
mCutyCapt = cutyCapt;
}
void
CutyPage::setPrintAlerts(bool printAlerts) {
mPrintAlerts = printAlerts;
}
void
CutyPage::setAttribute(QWebSettings::WebAttribute option,
const QString& value) {
if (value == "on")
settings()->setAttribute(option, true);
else if (value == "off")
settings()->setAttribute(option, false);
else
(void)0; // TODO: ...
}
// TODO: Consider merging some of main() and CutyCap
CutyCapt::CutyCapt(CutyPage* page, const QString& output, int delay, OutputFormat format,
const QString& scriptProp, const QString& scriptCode) {
mPage = page;
mOutput = output;
mDelay = delay;
mSawInitialLayout = false;
mSawDocumentComplete = false;
mFormat = format;
mScriptProp = scriptProp;
mScriptCode = scriptCode;
mScriptObj = new QObject();
// This is not really nice, but some restructuring work is
// needed anyway, so this should not be that bad for now.
mPage->setCutyCapt(this);
}
void
CutyCapt::InitialLayoutCompleted() {
mSawInitialLayout = true;
if (mSawInitialLayout && mSawDocumentComplete)
TryDelayedRender();
}
void
CutyCapt::DocumentComplete(bool /*ok*/) {
mSawDocumentComplete = true;
if (mSawInitialLayout && mSawDocumentComplete)
TryDelayedRender();
}
void
CutyCapt::JavaScriptWindowObjectCleared() {
if (!mScriptProp.isEmpty()) {
QVariant var = mPage->mainFrame()->evaluateJavaScript(mScriptProp);
QObject* obj = var.value<QObject*>();
if (obj == mScriptObj)
return;
mPage->mainFrame()->addToJavaScriptWindowObject(mScriptProp, mScriptObj);
}
mPage->mainFrame()->evaluateJavaScript(mScriptCode);
}
void CutyCapt::HtmlDownloadFinished(QNetworkReply * reply)
{
QVariant statusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
qDebug() << statusCode;
if ( !statusCode.isValid() )
{
QApplication::exit();
return;
}
int status = statusCode.toInt();
if( status == 301 || status == 302 )
{
QVariant loc = reply->header(QNetworkRequest::LocationHeader);
qDebug() << "Location :" << loc.toString();
QApplication::exit();
return;
}
if ( status != 200 )
{
QString reason = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
qDebug() << reason;
QApplication::exit();
return;
}
QString html=QString::fromUtf8(reply->readAll());
mPage->mainFrame()->setHtml( html, QUrl(mUrl) );
}
void CutyCapt::HtmlDownloadError(QNetworkReply::NetworkError code)
{
QApplication::exit();
}
void
CutyCapt::TryDelayedRender() {
if (!mPage->getAlertString().isEmpty())
return;
if (mDelay > 0) {
QTimer::singleShot(mDelay, this, SLOT(Delayed()));
return;
}
saveSnapshot();
QApplication::exit();
}
void
CutyCapt::Timeout() {
saveSnapshot();
QApplication::exit();
}
void
CutyCapt::Delayed() {
saveSnapshot();
QApplication::exit();
}
void
CutyCapt::saveSnapshot() {
QWebFrame *mainFrame = mPage->mainFrame();
QPainter painter;
const char* format = NULL;
for (int ix = 0; CutyExtMap[ix].id != OtherFormat; ++ix)
if (CutyExtMap[ix].id == mFormat)
format = CutyExtMap[ix].identifier; //, break;
// TODO: sometimes contents/viewport can have size 0x0
// in which case saving them will fail. This is likely
// the result of the method being called too early. So
// far I've been unable to find a workaround, except
// using --delay with some substantial wait time. I've
// tried to resize multiple time, make a fake render,
// check for other events... This is primarily a problem
// under my Ubuntu virtual machine.
mPage->setViewportSize( mainFrame->contentsSize() );
switch (mFormat) {
case SvgFormat: {
QSvgGenerator svg;
svg.setFileName(mOutput);
svg.setSize(mPage->viewportSize());
painter.begin(&svg);
mainFrame->render(&painter);
painter.end();
break;
}
case PdfFormat:
case PsFormat: {
QPrinter printer;
printer.setPageSize(QPrinter::A4);
printer.setOutputFileName(mOutput);
// TODO: change quality here?
mainFrame->print(&printer);
break;
}
case RenderTreeFormat:
case InnerTextFormat:
case HtmlFormat: {
QFile file(mOutput);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream s(&file);
s.setCodec("utf-8");
s << (mFormat == RenderTreeFormat ? mainFrame->renderTreeDump() :
mFormat == InnerTextFormat ? mainFrame->toPlainText() :
mFormat == HtmlFormat ? mainFrame->toHtml() :
"bug");
break;
}
default: {
QImage image(mPage->viewportSize(), QImage::Format_ARGB32);
painter.begin(&image);
mainFrame->render(&painter);
painter.end();
// TODO: add quality
image.save(mOutput, format);
}
};
}
void
CaptHelp(void) {
printf("%s",
" -----------------------------------------------------------------------------\n"
" Usage: CutyCapt --url=http://www.example.org/ --out=localfile.png \n"
" -----------------------------------------------------------------------------\n"
" --help Print this help page and exit \n"
" --url=<url> The URL to capture (http:...|file:...|...) \n"
" --out=<path> The target file (.png|pdf|ps|svg|jpeg|...) \n"
" --out-format=<f> Like extension in --out, overrides heuristic \n"
// " --out-quality=<int> Output format quality from 1 to 100 \n"
" --min-width=<int> Minimal width for the image (default: 800) \n"
" --min-height=<int> Minimal height for the image (default: 600) \n"
" --max-wait=<ms> Don't wait more than (default: 90000, inf: 0)\n"
" --delay=<ms> After successful load, wait (default: 0) \n"
// " --user-styles=<url> Location of user style sheet (deprecated) \n"
" --user-style-path=<path> Location of user style sheet file, if any \n"
" --user-style-string=<css> User style rules specified as text \n"
" --header=<name>:<value> request header; repeatable; some can't be set\n"
" --method=<get|post|put> Specifies the request method (default: get) \n"
" --body-string=<string> Unencoded request body (default: none) \n"
" --body-base64=<base64> Base64-encoded request body (default: none) \n"
" --app-name=<name> appName used in User-Agent; default is none \n"
" --app-version=<version> appVers used in User-Agent; default is none \n"
" --user-agent=<string> Override the User-Agent header Qt would set \n"
" --javascript=<on|off> JavaScript execution (default: on) \n"
" --java=<on|off> Java execution (default: unknown) \n"
" --plugins=<on|off> Plugin execution (default: unknown) \n"
" --private-browsing=<on|off> Private browsing (default: unknown) \n"
" --auto-load-images=<on|off> Automatic image loading (default: on) \n"
" --js-can-open-windows=<on|off> Script can open windows? (default: unknown) \n"
" --js-can-access-clipboard=<on|off> Script clipboard privs (default: unknown)\n"
#if QT_VERSION >= 0x040500
" --print-backgrounds=<on|off> Backgrounds in PDF/PS output (default: off) \n"
" --zoom-factor=<float> Page zoom factor (default: no zooming) \n"
" --zoom-text-only=<on|off> Whether to zoom only the text (default: off) \n"
" --http-proxy=<url> Address for HTTP proxy server (default: none)\n"
#endif
#if CUTYCAPT_SCRIPT
" --inject-script=<path> JavaScript that will be injected into pages \n"
" --script-object=<string> Property to hold state for injected script \n"
" --expect-alert=<string> Try waiting for alert(string) before capture \n"
" --debug-print-alerts Prints out alert(...) strings for debugging. \n"
#endif
" -----------------------------------------------------------------------------\n"
" <f> is svg,ps,pdf,itext,html,rtree,png,jpeg,mng,tiff,gif,bmp,ppm,xbm,xpm \n"
" -----------------------------------------------------------------------------\n"
#if CUTYCAPT_SCRIPT
" The `inject-script` option can be used to inject script code into loaded web \n"
" pages. The code is called whenever the `javaScriptWindowObjectCleared` signal\n"
" is received. When `script-object` is set, an object under the specified name \n"
" will be available to the script to maintain state across page loads. When the\n"
" `expect-alert` option is specified, the shot will be taken when a script in- \n"
" vokes alert(string) with the string if that happens before `max-wait`. These \n"
" options effectively allow you to remote control the browser and the web page.\n"
" This an experimental and easily abused and misused feature. Use with caution.\n"
" -----------------------------------------------------------------------------\n"
#endif
" http://cutycapt.sf.net - (c) 2003-2010 Bjoern Hoehrmann - bjoern#hoehrmann.de\n"
"");
}
int
main(int argc, char *argv[]) {
int argHelp = 0;
int argDelay = 0;
int argSilent = 0;
int argMinWidth = 800;
int argMinHeight = 600;
int argMaxWait = 90000;
int argVerbosity = 0;
const char* argUrl = NULL;
const char* argUserStyle = NULL;
const char* argUserStylePath = NULL;
const char* argUserStyleString = NULL;
const char* argIconDbPath = NULL;
const char* argInjectScript = NULL;
const char* argScriptObject = NULL;
QString argOut;
CutyCapt::OutputFormat format = CutyCapt::OtherFormat;
QApplication app(argc, argv, true);
CutyPage page;
QNetworkAccessManager::Operation method =
QNetworkAccessManager::GetOperation;
QByteArray body;
QNetworkRequest req;
QNetworkAccessManager manager;
// Parse command line parameters
for (int ax = 1; ax < argc; ++ax) {
size_t nlen;
const char* s = argv[ax];
const char* value;
// boolean options
if (strcmp("--silent", s) == 0) {
argSilent = 1;
continue;
} else if (strcmp("--help", s) == 0) {
argHelp = 1;
break;
} else if (strcmp("--verbose", s) == 0) {
argVerbosity++;
continue;
#if CUTYCAPT_SCRIPT
} else if (strcmp("--debug-print-alerts", s) == 0) {
page.setPrintAlerts(true);
continue;
#endif
}
value = strchr(s, '=');
if (value == NULL) {
// TODO: error
argHelp = 1;
break;
}
nlen = value++ - s;
// --name=value options
if (strncmp("--url", s, nlen) == 0) {
argUrl = value;
} else if (strncmp("--min-width", s, nlen) == 0) {
// TODO: add error checking here?
argMinWidth = (unsigned int)atoi(value);
} else if (strncmp("--min-height", s, nlen) == 0) {
// TODO: add error checking here?
argMinHeight = (unsigned int)atoi(value);
} else if (strncmp("--delay", s, nlen) == 0) {
// TODO: see above
argDelay = (unsigned int)atoi(value);
} else if (strncmp("--max-wait", s, nlen) == 0) {
// TODO: see above
argMaxWait = (unsigned int)atoi(value);
} else if (strncmp("--out", s, nlen) == 0) {
argOut = value;
if (format == CutyCapt::OtherFormat)
for (int ix = 0; CutyExtMap[ix].id != CutyCapt::OtherFormat; ++ix)
if (argOut.endsWith(CutyExtMap[ix].extension))
format = CutyExtMap[ix].id; //, break;
} else if (strncmp("--user-styles", s, nlen) == 0) {
// This option is provided for backwards-compatibility only
argUserStyle = value;
} else if (strncmp("--user-style-path", s, nlen) == 0) {
argUserStylePath = value;
} else if (strncmp("--user-style-string", s, nlen) == 0) {
argUserStyleString = value;
} else if (strncmp("--icon-database-path", s, nlen) == 0) {
argIconDbPath = value;
} else if (strncmp("--auto-load-images", s, nlen) == 0) {
page.setAttribute(QWebSettings::AutoLoadImages, value);
} else if (strncmp("--javascript", s, nlen) == 0) {
page.setAttribute(QWebSettings::JavascriptEnabled, value);
} else if (strncmp("--java", s, nlen) == 0) {
page.setAttribute(QWebSettings::JavaEnabled, value);
} else if (strncmp("--plugins", s, nlen) == 0) {
page.setAttribute(QWebSettings::PluginsEnabled, value);
} else if (strncmp("--private-browsing", s, nlen) == 0) {
page.setAttribute(QWebSettings::PrivateBrowsingEnabled, value);
} else if (strncmp("--js-can-open-windows", s, nlen) == 0) {
page.setAttribute(QWebSettings::JavascriptCanOpenWindows, value);
} else if (strncmp("--js-can-access-clipboard", s, nlen) == 0) {
page.setAttribute(QWebSettings::JavascriptCanAccessClipboard, value);
} else if (strncmp("--developer-extras", s, nlen) == 0) {
page.setAttribute(QWebSettings::DeveloperExtrasEnabled, value);
} else if (strncmp("--links-included-in-focus-chain", s, nlen) == 0) {
page.setAttribute(QWebSettings::LinksIncludedInFocusChain, value);
#if QT_VERSION >= 0x040500
} else if (strncmp("--print-backgrounds", s, nlen) == 0) {
page.setAttribute(QWebSettings::PrintElementBackgrounds, value);
} else if (strncmp("--zoom-factor", s, nlen) == 0) {
page.mainFrame()->setZoomFactor(QString(value).toFloat());
} else if (strncmp("--zoom-text-only", s, nlen) == 0) {
page.setAttribute(QWebSettings::ZoomTextOnly, value);
} else if (strncmp("--http-proxy", s, nlen) == 0) {
QUrl p = QUrl::fromEncoded(value);
QNetworkProxy proxy = QNetworkProxy(QNetworkProxy::HttpProxy,
p.host(), p.port(80), p.userName(), p.password());
manager.setProxy(proxy);
page.setNetworkAccessManager(&manager);
#endif
#if CUTYCAPT_SCRIPT
} else if (strncmp("--inject-script", s, nlen) == 0) {
argInjectScript = value;
} else if (strncmp("--script-object", s, nlen) == 0) {
argScriptObject = value;
} else if (strncmp("--expect-alert", s, nlen) == 0) {
page.setAlertString(value);
#endif
} else if (strncmp("--app-name", s, nlen) == 0) {
app.setApplicationName(value);
} else if (strncmp("--app-version", s, nlen) == 0) {
app.setApplicationVersion(value);
} else if (strncmp("--body-base64", s, nlen) == 0) {
body = QByteArray::fromBase64(value);
} else if (strncmp("--body-string", s, nlen) == 0) {
body = QByteArray(value);
} else if (strncmp("--user-agent", s, nlen) == 0) {
page.setUserAgent(value);
} else if (strncmp("--out-format", s, nlen) == 0) {
for (int ix = 0; CutyExtMap[ix].id != CutyCapt::OtherFormat; ++ix)
if (strcmp(value, CutyExtMap[ix].identifier) == 0)
format = CutyExtMap[ix].id; //, break;
if (format == CutyCapt::OtherFormat) {
// TODO: error
argHelp = 1;
break;
}
} else if (strncmp("--header", s, nlen) == 0) {
const char* hv = strchr(value, ':');
if (hv == NULL) {
// TODO: error
argHelp = 1;
break;
}
req.setRawHeader(QByteArray(value, hv - value), hv + 1);
} else if (strncmp("--method", s, nlen) == 0) {
if (strcmp("value", "get") == 0)
method = QNetworkAccessManager::GetOperation;
else if (strcmp("value", "put") == 0)
method = QNetworkAccessManager::PutOperation;
else if (strcmp("value", "post") == 0)
method = QNetworkAccessManager::PostOperation;
else if (strcmp("value", "head") == 0)
method = QNetworkAccessManager::HeadOperation;
else
(void)0; // TODO: ...
} else {
// TODO: error
argHelp = 1;
}
}
if (argUrl == NULL || argOut == NULL || argHelp) {
CaptHelp();
return EXIT_FAILURE;
}
// This used to use QUrl(argUrl) but that escapes %hh sequences
// even though it should not, as URLs can assumed to be escaped.
req.setUrl( QUrl::fromEncoded(argUrl) );
QString scriptProp(argScriptObject);
QString scriptCode;
if (argInjectScript) {
QFile file(argInjectScript);
if (file.open(QIODevice::ReadOnly)) {
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
stream.setAutoDetectUnicode(true);
scriptCode = stream.readAll();
file.close();
}
}
CutyCapt main(&page, argOut, argDelay, format, scriptProp, scriptCode);
main.mUrl = argUrl;
app.connect(&page,
SIGNAL(loadFinished(bool)),
&main,
SLOT(DocumentComplete(bool)));
app.connect(page.mainFrame(),
SIGNAL(initialLayoutCompleted()),
&main,
SLOT(InitialLayoutCompleted()));
if (argMaxWait > 0) {
// TODO: Should this also register one for the application?
QTimer::singleShot(argMaxWait, &main, SLOT(Timeout()));
}
if (argUserStyle != NULL)
// TODO: does this need any syntax checking?
page.settings()->setUserStyleSheetUrl( QUrl::fromEncoded(argUserStyle) );
if (argUserStylePath != NULL) {
page.settings()->setUserStyleSheetUrl( QUrl::fromLocalFile(argUserStylePath) );
}
if (argUserStyleString != NULL) {
QUrl data("data:text/css;charset=utf-8;base64," +
QByteArray(argUserStyleString).toBase64());
page.settings()->setUserStyleSheetUrl( data );
}
if (argIconDbPath != NULL)
// TODO: does this need any syntax checking?
page.settings()->setIconDatabasePath(argUserStyle);
// The documentation does not say, but it seems the mainFrame
// will never change, so we can set this here. Otherwise we'd
// have to set this in snapshot and trigger an update, which
// is not currently possible (Qt 4.4.0) as far as I can tell.
page.mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
page.mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
page.setViewportSize( QSize(argMinWidth, argMinHeight) );
#if CUTYCAPT_SCRIPT
// javaScriptWindowObjectCleared does not get called on the
// initial load unless some JavaScript has been executed.
page.mainFrame()->evaluateJavaScript(QString(""));
app.connect(page.mainFrame(),
SIGNAL(javaScriptWindowObjectCleared()),
&main,
SLOT(JavaScriptWindowObjectCleared()));
#endif
app.connect( &manager,SIGNAL(finished(QNetworkReply *)), &main,SLOT(HtmlDownloadFinished(QNetworkReply *)));
QNetworkReply *reply = manager.get(req);
app.connect( reply,SIGNAL(error(QNetworkReply::NetworkError)), &main,SLOT(HtmlDownloadError(QNetworkReply::NetworkError)));
return app.exec();
}

QSignalSpy to capture a reference argument

It is not possible to capture an argument that has been passed as reference with a QSignalSpy:
QSignalSpy spy( myObject, SIGNAL(foo(int&)));
...
int& i=spy.at(0).at(0).value<int&>();
Since a QVariant can not contain a reference member. Plain logic.
But are there other solutions to check the passed-in argument?
Since Qt 5, we can simply connect to a lambda function, which makes the use of the QSignalSpy unnecessary:
std::vector<Value> values;
QObject::connect(myObject, &MyObject::foo,
[&](const auto &value)
{ values.emplace_back(value); });
myObject.somethingCausingFoo();
ASSERT_EQ(1u, values.size());
EXPECT_EQ(expectedValue, values.at(0));
An "ugly solution" would be to hack the fairly simple QSignalSpy code in order to handle the reference passed arguments. I provide a minimal working example for int reference arguments. The only changes were made to initArgs and appendArgs functions.
Notice that with this approach you will only be able to check the value of the passed argument by reference. You will not be able to change it's value.
In the initArgs function we check if we have references by argument and we populate the shouldreinterpret list.
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
// Check if we have a reference by removing the & from the parameter name
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
and the appendArgs function, where we reinterpret the passed by reference arguments:
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
list << QVariant(type, &(*reinterpret_cast<int*>(a[i + 1])));
break;
// Do the same for other types
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
Complete code for reference:
class MySignalSpy: public QObject, public QList<QList<QVariant> >
{
public:
MySignalSpy(QObject *obj, const char *aSignal)
{
#ifdef Q_CC_BOR
const int memberOffset = QObject::staticMetaObject.methodCount();
#else
static const int memberOffset = QObject::staticMetaObject.methodCount();
#endif
Q_ASSERT(obj);
Q_ASSERT(aSignal);
if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
qWarning("QSignalSpy: Not a valid signal, use the SIGNAL macro");
return;
}
QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
const QMetaObject *mo = obj->metaObject();
int sigIndex = mo->indexOfMethod(ba.constData());
if (sigIndex < 0) {
qWarning("QSignalSpy: No such signal: '%s'", ba.constData());
return;
}
if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
Qt::DirectConnection, 0)) {
qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
sig = ba;
initArgs(mo->method(sigIndex));
}
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }
int qt_metacall(QMetaObject::Call call, int methodId, void **a)
{
methodId = QObject::qt_metacall(call, methodId, a);
if (methodId < 0)
return methodId;
if (call == QMetaObject::InvokeMetaMethod) {
if (methodId == 0) {
appendArgs(a);
}
--methodId;
}
return methodId;
}
private:
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
int k = (*reinterpret_cast<int*>(a[i + 1]));
list << QVariant(type, &k);
break;
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
// the full, normalized signal name
QByteArray sig;
// holds the QMetaType types for the argument list of the signal
QList<int> args;
// Holds the indexes of the arguments that
QList<bool> shouldReinterpret;
};

find_if in MFC container with iterator derived from std::iterator

I have a working iterator for MFC CObList - BaseMFCIter. It works for iterating in loop but i still didn't managed to make ListIter to work properly with STL algorithm find_if.
Code
#include < iterator >
#include "afxwin.h"
#include "afxtempl.h"
#include <iostream>
#include <algorithm>
#include <cstdlib>
class myCObject : public CObject
{
public:
myCObject( std::string val )
{
x = val;
}
std::string x;
};
template < typename Item, class Cont, class Key = POSITION >
class BaseMFCIter : public std::iterator < std::input_iterator_tag, Item >
{
public:
// Define types for the 2 member functions to be used:
typedef Key (Cont::*GetFirstFunctionPtr) () const;
typedef Item (Cont::*GetNextFunctionPtr) (Key&) const;
// Default constructor, makes a null iterator, equal to BaseMFCIter::end()
BaseMFCIter() : m_pCont(0), m_Pos(0), m_GetFirstFunc(0), m_GetNextFunc(0), m_End(true) {}
// Constructor taking pointer to container and the iteration functions
BaseMFCIter(Cont* pCont, GetFirstFunctionPtr pFF, GetNextFunctionPtr pNF)
: m_pCont(pCont), m_Pos(0), m_GetFirstFunc(pFF), m_GetNextFunc(pNF)
{ init(); }
// Copy constructor, initialises iterator to first element
BaseMFCIter(const BaseMFCIter& vi) : m_pCont(vi.m_pCont), m_Pos(0),
m_GetFirstFunc(vi.m_GetFirstFunc), m_GetNextFunc(vi.m_GetNextFunc)
{ init(); }
// Assignment operator, initialises iterator to first element
BaseMFCIter& operator=(const BaseMFCIter& vi)
{
m_pCont = vi.m_pCont;
m_GetFirstFunc = vi.m_GetFirstFunc;
m_GetNextFunc = vi.m_GetNextFunc;
init();
return *this;
}
bool operator == (const BaseMFCIter& rhs) const
{ return (m_Pos == rhs.m_Pos && m_End == rhs.m_End); }
bool operator != (const BaseMFCIter& rhs) const
{ return !operator==(rhs); }
BaseMFCIter& operator ++ () { advance(); return *this; }
BaseMFCIter& operator ++ (int) { BaseMFCIter ret(*this); advance(); return ret; }
Item operator * () { return m_Item; }
Item operator -> () { return m_Item; }
static BaseMFCIter end () { return BaseMFCIter(); } // end() returns default null iterator
private:
Item m_Item; // Current item from container
Cont* m_pCont; // Pointer to container
Key m_Pos; // Key to item in container
bool m_End; // Flag to indicate end of container reached
// Pointers to container iteration functions
GetFirstFunctionPtr m_GetFirstFunc;
GetNextFunctionPtr m_GetNextFunc;
// Use container GetFirst & GetNext functions to set to first element, or end() if not found
void init()
{
m_Pos = 0;
m_End = true;
if (m_pCont && m_GetFirstFunc != 0)
{
m_Pos = (m_pCont->*m_GetFirstFunc)();
advance();
}
}
// Use container GetNext function to find next element in container
void advance()
{
m_End = m_Pos ? false : true;
m_Item = (m_Pos && m_pCont && m_GetNextFunc != 0) ?
(m_pCont->*m_GetNextFunc)(m_Pos) : Item();
}
};
struct Container : public CObList
{
myCObject* GetNext(POSITION& rPosition)
{
return dynamic_cast<myCObject*>(CObList::GetNext(rPosition));
}
myCObject const* GetNext(POSITION& rPosition) const
{
return dynamic_cast<const myCObject*>(CObList::GetNext(rPosition));
}
};
class ListIter : public BaseMFCIter < const myCObject*, Container, POSITION >
{
public:
ListIter( Container* pObj = 0)
: BaseMFCIter< const myCObject*, Container, POSITION >
(pObj, &CObList::GetHeadPosition, &Container::GetNext)
{
}
};
struct Comparator
{
std::string stringToCompare;
bool operator() ( const myCObject* lhs )
{
return (bool) lhs->x.compare( stringToCompare );
}
};
void main( )
{
myCObject* m = new myCObject( "one" );
myCObject* n = new myCObject( "two" );
myCObject* p = new myCObject( "three" );
myCObject* q = new myCObject( "four" );
Container cont;
cont.AddHead( m );
cont.AddHead( n );
cont.AddHead( p );
cont.AddHead( q );
Comparator pred;
pred.stringToCompare = "1";
ListIter iter = ListIter( &cont );
ListIter endIter = ListIter( );
ListIter foundIter = std::find_if( iter, endIter, pred );
std::cout << "foundIter x is: " << foundIter->x.c_str() << std::endl;
}
gives me foundIter x is: four. This propably happens because of the way the end position is defined so
_InIt _Find_if(_InIt _First, _InIt _Last, _Pr _Pred)
{ // find first satisfying _Pred
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Pred);
for (; _First != _Last; ++_First)
if (_Pred(*_First))
break;
return (_First);
}
doesn't iterate properly but i can't figure out how to fix it.
A number of issues fixed:
(bool) lhs->x.compare( stringToCompare ) returns true _whenever the string don't match** (see string::compare)
you were searching for "1", which doesn't exist
since the predicate was wrong, you received the first match, which was the first element, also inserted the last, and the name was "four" :)
you didn't check whether a valid match was found (dereferencing the end-iterator is illegal and may crash your program or do worse things: undefined behaviour)
you had a superflous x.c_str() in the output statement
I changed the Compare predicate around to be more idiomatic:
initialize stringToCompare from the constructor
make the field const
make the operator() a const method
This should do the trick (untested code, I'm not near a compiler the coming hours)
Update
After arriving home, I finally broke out the debugger to track that strange behaviour (see comments).
To my dismay, I found out that the BaseMFCIter was designed by someone with very limited understanding of what an iterator is: the copy constructor and assignment operator were completely wrong: they had the effect of creating a new begin iterator - for the same collection. This however, means that an iterator could never be returned from a function.
Therefore, I fixed it (first by implementing it right, later by removing the now-redundant constructor and operator= in favour of the compiler-generated default implementations).
See the full history of my and your edits:
git clone git://gist.github.com/1353471.git
sehe 11 minutes ago rely on default generated copy constructor and assignment instead
sehe 12 minutes ago fixed broken copy constructor and assignment
sehe 65 minutes ago tentative
Dmitry 73 minutes ago Attempt at find_if with predicate
sehe Heeren 25 hours ago Fixed and Tested (VS2010)
sehe 25 hours ago ( STL iterator for MFC container CObList )
#include "afxwin.h"
#include "afxtempl.h"
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <string>
#include <iterator>
class myCObject : public CObject
{
public:
myCObject( const std::string& val ) { x = val; }
std::string x;
};
template < typename Item, class Cont, class Key = POSITION >
class BaseMFCIter : public std::iterator < std::input_iterator_tag, Item >
{
public:
// Define types for the 2 member functions to be used:
typedef Key (Cont::*GetFirstFunctionPtr) () const;
typedef Item (Cont::*GetNextFunctionPtr) (Key&) const;
// Default constructor, makes a null iterator, equal to BaseMFCIter::end()
BaseMFCIter() : m_pCont(0), m_Pos(0), m_GetFirstFunc(0), m_GetNextFunc(0), m_End(true) {}
// Constructor taking pointer to container and the iteration functions
BaseMFCIter(Cont* pCont, GetFirstFunctionPtr pFF, GetNextFunctionPtr pNF)
: m_pCont(pCont), m_Pos(0), m_GetFirstFunc(pFF), m_GetNextFunc(pNF)
{ init(); }
bool operator == (const BaseMFCIter& rhs) const
{ return (m_Pos == rhs.m_Pos && m_End == rhs.m_End); }
bool operator != (const BaseMFCIter& rhs) const
{ return !operator==(rhs); }
BaseMFCIter& operator ++ () { advance(); return *this; }
BaseMFCIter& operator ++ (int) { BaseMFCIter ret(*this); advance(); return ret; }
Item operator * () { return m_Item; }
Item operator -> () { return m_Item; }
static BaseMFCIter end () { return BaseMFCIter(); } // end() returns default null iterator
private:
Item m_Item; // Current item from container
Cont* m_pCont; // Pointer to container
Key m_Pos; // Key to item in container
bool m_End; // Flag to indicate end of container reached
// Pointers to container iteration functions
GetFirstFunctionPtr m_GetFirstFunc;
GetNextFunctionPtr m_GetNextFunc;
// Use container GetFirst & GetNext functions to set to first element, or end() if not found
void init()
{
m_Pos = 0;
m_End = true;
if (m_pCont && m_GetFirstFunc != 0)
{
m_Pos = (m_pCont->*m_GetFirstFunc)();
advance();
}
}
// Use container GetNext function to find next element in container
void advance()
{
m_End = m_Pos ? false : true;
m_Item = (m_Pos && m_pCont && m_GetNextFunc != 0) ?
(m_pCont->*m_GetNextFunc)(m_Pos) : Item();
}
};
struct Container : public CObList
{
myCObject* GetNext(POSITION& rPosition)
{
return dynamic_cast<myCObject*>(CObList::GetNext(rPosition));
}
myCObject const* GetNext(POSITION& rPosition) const
{
return dynamic_cast<const myCObject*>(CObList::GetNext(rPosition));
}
};
class ListIter : public BaseMFCIter < const myCObject*, Container, POSITION >
{
public:
ListIter( Container* pObj = 0)
: BaseMFCIter< const myCObject*, Container, POSITION >
(pObj, &CObList::GetHeadPosition, &Container::GetNext)
{
}
};
struct Comparator
{
Comparator(const std::string& compareTo) : stringToCompare(compareTo) {}
bool operator() ( const myCObject* lhs ) const
{
return 0 == lhs->x.compare( stringToCompare );
}
private:
const std::string stringToCompare;
};
void main( )
{
myCObject* m = new myCObject( "one" );
myCObject* n = new myCObject( "two" );
myCObject* p = new myCObject( "three" );
myCObject* q = new myCObject( "four" );
Container cont;
cont.AddHead( m );
cont.AddHead( n );
cont.AddHead( p );
cont.AddHead( q );
Comparator pred("three");
ListIter iter = ListIter(&cont),
endIter = ListIter( );
ListIter foundIter = std::find_if( iter, endIter, pred );
if (endIter != foundIter)
{
std::cout << "foundIter x is: " << foundIter->x << std::endl;
}
else
{
std::cout << "not found" << std::endl;
}
}

Resources