I have recently started testing the OTL library with the SQL Server using the Visual Studio 2013. My tests demonstrated that the performance of simple select statements against a 10000 count table is 40% slower than the performance of a similar .NET 4.0 test application. All tests were performed with all optimizations turned on for both platforms.
Both apps perform the following tasks:
Open the db connection
Create (and reserve space) for the container object.
Execute the select statement command.
For each record fetched from db
create an entity using the db(stream/reader) object
add the object to container
close
.NET C# app requires 0.5 secs to complete this task, while OTL-C++ app takes 0.7 secs to complete and I wonder if it is possible to optimize the C++ app to perform faster?
Snippet of C++ code:
#define OTL_ODBC_MSSQL_2008 // Compile OTL 4/ODBC, MS SQL 2008
#define OTL_CPP_11_ON
#define OTL_STL // Turn on STL features
#define OTL_ANSI_CPP // Turn on ANSI C++ typecasts
#define OTL_UNICODE // Enable Unicode OTL for ODBC
#include "otlv4.h"
class Employee
{
private:
int employeeId;
wstring regno;
wstring name;
wstring surname;
public:
Employee()
{
}
Employee(otl_stream& stream)
{
unsigned short _regno[32];
unsigned short _name[32];
unsigned short _surname[32];
if (!stream.is_null())
{
stream >> employeeId;
}
if (!stream.is_null())
{
stream >> (unsigned char*)_regno;
regno = (wchar_t*)_regno;
}
if (!stream.is_null()){
stream >> (unsigned char*)_name;
name = (wchar_t*)_name;
}
if (!stream.is_null()){
stream >> (unsigned char*)_surname;
surname = (wchar_t*)_surname;
}
}
int GetEmployeeId() const
{
return employeeId;
}
};
otl_connect db;
int main()
{
otl_connect::otl_initialize();
try
{
otl_connect::otl_initialize();
try
{
// connect
db.rlogon("DSN=SQLODBC");
// start timing
clock_t begin = clock();
otl_stream i(10000, "SELECT Id, Field1, Field2, Field3 FROM Test", db);
// create container
vector<Employee> employeeList;
employeeList.reserve(10000);
// iterate and fill container
while (!i.eof())
{
Employee employee(i);
employeeList.push_back(employee);
}
i.close();
// cleanup
size_t size = employeeList.size();
clock_t end = clock();
double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
cout << "Total records:" << size << endl;
cout << "Time elapsed to read all records:" << elapsed_secs << endl;
}
catch (otl_exception& p){ // intercept OTL exceptions
cerr << p.msg << endl; // print out error message
cerr << p.stm_text << endl; // print out SQL that caused the error
cerr << p.sqlstate << endl; // print out SQLSTATE message
cerr << p.var_info << endl; // print out the variable that caused the error
}
db.logoff();
return EXIT_SUCCESS;
}
I don't think so, when you look the code source of otl, it does use ODBC api for SQL Server and is only optimized as a odbc top layer. The SQL Server .NET 4.0 will use the sql driver api instead of odbc api for performance reason.
Also if you don't preallocate your memory consumption, you will always loose to .NET and Java due to the SysAllocMem function call. It's like trying to measure 4000 call to SysAlloc vs 1 call to SysAlloc. Your performance issue is directly linked to those functions.
Related
Consider the following two snippets of code where I am trying to launch 10000 threads:
Snippet 1
std::array<std::future<void>, 10000> furArr_;
try
{
size_t index = 0;
for (auto & fut : furArr_)
{
std::cout << "Created thread # " << index++ << std::endl;
fut = std::async(std::launch::async, fun);
}
}
catch (std::system_error & ex)
{
std::string str = ex.what();
std::cout << "Caught : " << str.c_str() << std::endl;
}
// I will call get afterwards, still 10000 threads should be active by now assuming "fun" is time consuming
Snippet 2
std::array<std::thread, 10000> threadArr;
try
{
size_t index = 0;
for (auto & thr : threadArr)
{
std::cout << "Created thread # " << index++ << std::endl;
thr = std::thread(fun);
}
}
catch (std::system_error & ex)
{
std::string str = ex.what();
std::cout << "Caught : " << str.c_str() << std::endl;
}
The first case always succeeds .i.e. I am able to create 10000 threads and then I have to wait for all of them to finish. In the second case, almost always I end up getting an exception("resource unavailable try again") after creating 1600+ threads.
With a launch policy of std::launch::async, I thought that the two snippets should behave the same way. How different std::async with a launch policy of async is from launching a thread explicitly using std::thread?
I am on Windows 10, VS2015, binary is built in x86 release mode.
Firstly, thanks to Igor Tandetnik for giving me the direction for this answer.
When we use std::async (with async launch policy), we are saying:
“I want to get this work done on a separate thread”.
When we use std::thread we are saying:
“I want to get this work done on a new thread”.
The subtle difference means that async (is usually) implemented using thread pools. Which means if we have invoked a method using async multiple times, often the thread id inside that method will repeat i.e. async allocates multiple jobs to the same set of threads from the pool. Whereas with std::thread, it never will.
This difference means that launching threads explicitly will be potentially more resource intensive (and thus the exception) than using async with async launch policy.
I need to lock stdout in my logging application to prevent string interleaving in multi-thread applications logging to stdout. Can't figure out how to use move constructor or std::move or sth else to move unique_lock to another object.
I created objects for setting configs and encapsulation and figured out how to lock stdout with static std::mutex to lock from these objects (called shards).
Something like this works for me:
l->log(1, "Test message 1");
While that is fine and could be implemented with templates and variable number of parameters I would like to approach more stream-like possibilities. I am looking for something like this:
*l << "Module id: " << 42 << "value: " << 42 << std::endl;
I dont want to force users to precompute string with concatenation and to_string(42) I just want to find a way to lock stdout.
My approach so far was to create operator << and another object locked stream, as was suggested in other answers. Things is I can't figure how to move mutex to another object. My code:
locked_stream& shard::operator<<(int num)
{
static std::mutex _out_mutex;
std::unique_lock<std::mutex> lock(_out_mutex);
//std::lock_guard<std::mutex> lock (_out_mutex);
std::cout << std::to_string(num) << "(s)";
locked_stream s;
return s;
}
After outputting input to std::cout I woould like to move lock into object stream.
In this case, I would be careful not to use static locks in functions, as you will get a different lock for each stream operator you create.
What you need is to lock some "output lock" when a stream is created, and unlock it when the stream is destroyed. You can piggie back on existing stream operations if you're just wrapping std::ostream. Here's a working implementation:
#include <mutex>
#include <iostream>
class locked_stream
{
static std::mutex s_out_mutex;
std::unique_lock<std::mutex> lock_;
std::ostream* stream_; // can't make this reference so we can move
public:
locked_stream(std::ostream& stream)
: lock_(s_out_mutex)
, stream_(&stream)
{ }
locked_stream(locked_stream&& other)
: lock_(std::move(other.lock_))
, stream_(other.stream_)
{
other.stream_ = nullptr;
}
friend locked_stream&& operator << (locked_stream&& s, std::ostream& (*arg)(std::ostream&))
{
(*s.stream_) << arg;
return std::move(s);
}
template <typename Arg>
friend locked_stream&& operator << (locked_stream&& s, Arg&& arg)
{
(*s.stream_) << std::forward<Arg>(arg);
return std::move(s);
}
};
std::mutex locked_stream::s_out_mutex{};
locked_stream locked_cout()
{
return locked_stream(std::cout);
}
int main (int argc, char * argv[])
{
locked_cout() << "hello world: " << 1 << 3.14 << std::endl;
return 0;
}
Here it is on ideone: https://ideone.com/HezJBD
Also, forgive me, but there will be a mix of spaces and tabs up there because of online editors being awkward.
The below code is a sample code from the msdn(link) about how to query operating information using WMI. When running this code in visual c++ 2010, i get the following error :
"Unhandled exception at 0x5a2c47af (msvcr100d.dll) in ProjectTest.exe:
0xC0000005: Access violation reading location 0xccccffff."
Can anyone help me please with this issue?
#include "stdafx.h"
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
# pragma comment(lib, "wbemuuid.lib")
int main()
{
HRESULT hres;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
cout << "Failed to initialize COM library. Error code = 0x"
<< hex << hres << endl;
return 1; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
// Note: If you are using Windows 2000, you need to specify -
// the default authentication credentials for a user by using
// a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
// parameter of CoInitializeSecurity ------------------------
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (FAILED(hres))
{
cout << "Failed to initialize security. Error code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object."
<< " Err code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_OperatingSystem"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
cout << "Query for operating system name failed."
<< " Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if(0 == uReturn)
{
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"PAEEnabled", 0, &vtProp, 0, 0);
wcout << " Is PAE enabled? : " << vtProp.bstrVal << endl;
VariantClear(&vtProp);
pclsObj->Release();
}
// Cleanup
// ========
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();
system("PAUSE");
return 0; // Program successfully completed.
}
Actually it does return perfectly valid data.
Its just that it only returns a boolean for your call and no bstrVal.
Check the vtProp.vt property against VT_BOOLEAN.
In a VARIANT you always have to check the vt property for the type of data which the VARIANT holds.
ORIGINAL Answer:
Your vtProp is a nullptr even tho the HRESULT of the pclsObj->Get() call is S_OK. Most likely it does not find the PAEEnabled property.
When you use the original code from the MSDN example where it uses "Name" instead of "PAEEnabled" it works just fine.
I have a third party component written in C/C++ (on VS 2010) which can be downloaded here.
This component accepts 3 parameters as input (a filename and two numbers) and outputs a result in the console, and then outputs a file.
I've used Process and ProcessStartInfo in a C# WinForm project to consume this component which works fine. However, now I want to consume this in a WCF C# RESTful service, in which case the solution I thought with WinForm will not work.
It was suggested that I instead convert this to a MFC DLL and then use InterOp to call the unmanaged DLL through my C# web service (other suggestions are welcome).
Unfortunately, I have no idea on how to do that and my knowledge on C/C++ is fairly average. So my question is: How do I create a DLL from that component which accepts these 3 parameters (taken from main()):
cin >> fname;
cin >> minA;
cin >> minO;
then does whatever calculations it's supposed to do and return this (again taken from main()):
cout << "\nNumber is: " << num;
(and obviously still output the file it's supposed to output) ?
Any help would be HIGHLY appreciated.
Thanks in advance!
UPDATE: As a point of reference, here is my WinForm implementation mentioned above.
ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
Process cmdProcess = new Process();
BackgroundWorker BWorker = new BackgroundWorker();
//is able to report progress
BWorker.WorkerReportsProgress = true;
//is able to be cancelled
BWorker.WorkerSupportsCancellation = true;
//attach events
BWorker.DoWork += worker_DoWork;
BWorker.RunWorkerCompleted += worker_RunWorkerCompleted;
BWorker.RunWorkerAsync();
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
if (firstTimeLoaded)
{
cmdStartInfo.FileName = Path.GetFullPath("../../Resources/thirdparty.exe");
cmdStartInfo.WorkingDirectory = Path.GetFullPath("../../Resources/");
cmdStartInfo.RedirectStandardOutput = true;
cmdStartInfo.RedirectStandardError = true;
cmdStartInfo.RedirectStandardInput = true;
cmdStartInfo.UseShellExecute = false;
cmdStartInfo.CreateNoWindow = true;
cmdProcess.StartInfo = cmdStartInfo;
cmdProcess.SynchronizingObject = this;
cmdProcess.ErrorDataReceived += cmd_Error;
cmdProcess.Exited += cmd_Exited;
cmdProcess.EnableRaisingEvents = true;
cmdProcess.Start();
cmdProcess.BeginErrorReadLine();
firstTimeLoaded = false;
}
while (!cmdProcess.HasExited)
{
if (use)
{
if (BWorker.CancellationPending)
{
e.Cancel = true;
}
StringBuilder builder = new StringBuilder();
//read unbuffered output
while (cmdProcess.StandardOutput.Peek() != -1)
{
char inputChar = (char)cmdProcess.StandardOutput.Read();
if (inputChar != '\r' && inputChar != '\n')
{
builder.Append(inputChar);
}
if (inputChar == '\n')
{
break;
}
}
if (cmdProcess.StandardOutput.Peek() == -1)
{
cmdProcess.StandardOutput.DiscardBufferedData();
}
//process the output
string output = builder.ToString();
//determine appropriate action
switch (output)
{
case "Enter file name: ":
cmdProcess.StandardInput.WriteLine(textBox1.Text);
break;
case "Enter minimum size of A: ":
cmdProcess.StandardInput.WriteLine(textBox2.Text);
break;
case "Enter minimum size of O: ":
cmdProcess.StandardInput.WriteLine(textBox3.Text);
break;
}
if (output.Contains("Number: "))
{
MessageBox.Show("Number is: " + output.Substring(output.LastIndexOf(" ") + 1));
use = false;
}
}
}
}
Let's give this a try.
In VS2010, create a Win32 Project under Visual C++/Win32. For this purpose, call it MyWin32Lib.
Add the thirdparty.cpp file to the project and compile. You should get some warnings, but it's ok.
Create a new header file called thirdparty.h so we can export the function signature.
In the thirdparty.h file, do:
#pragma once
// This will be the interface for third party file
int concepts(char* szFileName, int nMinIntent, int nMinExtent);
In the thirdparty.cpp file, add #include "stdafx.h" right before #include
Change the main function signature to match the one in the header:
//int main()
// Instead of getting input from console, we're passing it the values
int concepts(char* szFileName, int nMinIntent, int nMinExtent)
Comment out all input requests, and just copy the args to the local vars:
//cout << "\n\n***** In-Close 3.0 Concept Miner *****";
//cout << "\n\nEnter cxt file name including extension: ";
//cin >> fname;
//cout << "\nEnter minimum size of intent (no. attributes): ";
//cin >> minIn;
//cout << "\nEnter minimum size of extent (no. objects): ";
//cin >> minEx;
strcpy_s(fname, _countof(fname), szFileName);
minIn = nMinIntent;
minEx = nMinExtent;
Comment out cout << "\nNumber... (this is no longer needed)
At the end of the function, do:
break;
}
//cout << "\n\nHit <enter> to finish";
//while ( !_kbhit());
return numcons;
}
I don't know why there's a while(1) since there's no way to get out of it, but assume we'll doing it only once.
Make sure you compile ok.
Create a new CPP file, call it "Concepts.cpp"
In Concepts.cpp, enter:
#include "stdafx.h"
#include "thirdparty.h"
extern "C"
{
__declspec(dllexport) int GetConcepts(char* szFileName, int nMinIntent, int nMinExtent)
{
return concepts(szFileName, nMinIntent, nMinExtent);
}
}
*You should now have a Win32 DLL that performs the work using arguments instead.
Create a C# Class Library project.
Create a C# class called "Concepts.cs"
In this class, enter:
public class Concepts
{
// Link to the Win32 library through InterOp
[DllImport("MyWin32Lib.dll")]
public static extern int GetConcepts(
[MarshalAs( UnmanagedType.LPStr )] string fileName, int minIntent, int minExtent );
}
*You have to marshal the filename input as ANSI since that's what thirdparty.cpp uses.
I think I got all of it. You can now reference your C# library from a web service.
I am very new to QT and SQLite DBMS. I am trying to open an existing database created using "sqlite3" command-line program under ubuntu Linux. The same database I am trying to access under QT using the following code :
void MainWindow::func()
{
QSqlQuery query;
accounts_db = new QSqlDatabase();
*accounts_db = QSqlDatabase::addDatabase("QSQLITE");
perror("? ");
accounts_db->setDatabaseName("/home/user/xyz.db");
QSqlError *a = new QSqlError();
*a = accounts_db->lastError();
perror(a->text().toLatin1());
if (!accounts_db->open()) {
perror("database open error :");
}
if ( !accounts_db->isOpen() ) {
perror("database is not open");
}
query.exec("select accno,branchcode,fname,lname,curbalance,accdate from accounts");
while(query.next()) {
QString str = query.value(0).toString();
std::cerr << qPrintable(str) << std::endl;
}
end:
;
}
This fails with the following errors...
No such file or directory
: Invalid argument
QSqlQuery::exec: database not open
Notice that I get "No such file or directory" after adddatabase(), have no clue whatsoever which file is it talking about. Also notice that isOpen() and open() are returning "true" (???). The "database not open" error is from db.exec() call (...I suppose...).
In desperate need of guidance...
The constructor of QSqlQuery with no parameters uses the default database for your application. Maybe it is not set yet. Use the constructor specifying the database the query is required to use:
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "connection_name");
// Open db...
QSqlQuery query(db);
if (!query.exec(...)) {
// ...
}
// ...
Pay attention to how you close the connection afterwards.
EDIT: This is a test I just wrote and is working on my system. You might want to try.
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QVariant>
int main(int argc, char *argv[])
{
// Create database.
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "Connection");
db.setDatabaseName("/tmp/test.db");
if (!db.open()) {
qDebug("Error occurred opening the database.");
qDebug("%s.", qPrintable(db.lastError().text()));
return -1;
}
// Insert table.
QSqlQuery query(db);
query.prepare("CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, text TEXT)");
if (!query.exec()) {
qDebug("Error occurred creating table.");
qDebug("%s.", qPrintable(db.lastError().text()));
return -1;
}
// Insert row.
query.prepare("INSERT INTO test VALUES (null, ?)");
query.addBindValue("Some text");
if (!query.exec()) {
qDebug("Error occurred inserting.");
qDebug("%s.", qPrintable(db.lastError().text()));
return -1;
}
// Query.
query.prepare("SELECT * FROM test");
if (!query.exec()) {
qDebug("Error occurred querying.");
qDebug("%s.", qPrintable(db.lastError().text()));
return -1;
}
while (query.next()) {
qDebug("id = %d, text = %s.", query.value(0).toInt(),
qPrintable(query.value(1).toString()));
}
return 0;
}
This is mostly guessing, since your code is wrong on quite a few things, including the error reporting.
The most likely problem is that your file path is simply not right, or the user you're running your application with does not have the appropriate permissions on the file and/or directory. (Note: files and directory are case sensitive in Linux.)
perror should only be used after calling a system function that actually failed and that sets errno when it does. Qt doesn't do that.
Please try running this, and update your question if you still cannot resolve your issue:
void MainWindow::func()
{
// Note: no pointer!
QSqlDatabase accounts_db = QSqlDatabase::addDatabase("QSQLITE");
accounts_db.setDatabaseName("/home/user/xyz.db");
if (!accounts_db.open())
{
qDebug() << "Could not open database file:";
qDebug() << accounts_db.lastError();
return;
}
// Note: don't construct queries before you have a database!
QSqlQuery query;
if (!query.exec("select accno,branchcode,fname,lname,curbalance,accdate from accounts"))
{
qDebug() << "Query failed:";
qDebug() << query.lastError();
return;
}
while(query.next()) {
QString str = query.value(0).toString();
std::cerr << qPrintable(str) << std::endl;
}
}
(I haven't even tried to compile this, so YMMV.)
Have a look at the SQL examples also, and look at how they handle all this there.
Alright, I created a brand new database file using sqlite3 command, now the same program in my question is working !!!.
I had also tried this before, but that time it did not work (...I hate it when that happens). I guess the previous file may be corrupted. But the previous database file could be accessed from cmdline sqlite3 program, so I was assuming that file was ok,...but apparently not.
Anyway, thanks a lot guys for giving me time, and very sorry if I wasted it :( .
I am marking this as an answer just for the sake of clarity that this question is answered. But its not exactly an answer (...because I still don't understand what was happening !? )
Thanks again...
EDIT :
Here is the code
QSqlError *a = new QSqlError();
accounts_db = new QSqlDatabase();
*accounts_db = QSqlDatabase::addDatabase("QSQLITE");
accounts_db->setDatabaseName("/home/user/test.db");
if ( !accounts_db->open() ) {
qDebug() << accounts_db->lastError();
qDebug() << "Could not open database file:";
}
QSqlQuery query;
if ( !(accounts_db->isOpen()) ) {
qDebug() << accounts_db->lastError();
qDebug() << ": Could not open database file:";
goto end; // quit if not successful
}
query.exec("select * from accounts");
while(query.next()) {
// loop for i columns
QString str = query.value(i).toString();
std::cerr << qPrintable(str) << std::endl ;
// loop
}
end:
;