additional text shouldn't be visible
We all know the problem that the Delphi string types (UnicodeString or short String, AnsiString) do not necessarily fit to C++, and we have to use the method c_str() again and again. This is not only annoying, it can also lead to errors. In addition, we often want to have the content of a string in a different data type, or vice versa convert it into a Delphi string, for example to display it in an edit field of a form.
In addition, thanks to Embarcadero's decision to continue using a 16 bit character type under iOS and Android, instead of the system-specific wchar_t type, which is a 32 bit data type in the unixoid operating systems, this is not necessarily understandable. So it can happen that in a C++ program under Android we suddenly have it with 3 not necessarily compatible datatypes. In the business part of your application you should only have the choice between the national character set (8 bit, char, string) or the international character set (wchar_t, wstring). Furthermore, the indices for the Delphi string types are based on the desktop operating systems 1, but on the mobile operating systems they are based on C and C++ oriented 0. This can also lead to problems and necessary adjustments. All this would be simply impossible under C++, since an industry standard protects the developer's investment.
I would like to create a header file for further use in this blog, and this problem with the help of C++- templates and the specialization of these for special data types.
Maybe this one in front. I was once, so not only fairy tales begin. Often we as business consultants also start with such sentences. I was once at a customer's for an in-house training. Here several developers have been working on a program from the energy sector for several years. During the training, they also discussed the fact that for the Embarcadero string types, the constructors for the standard C++ data types are missing, or the conversion operators for the reverse direction are not implemented. Unfortunately, the operators in particular cannot be added via argument dependent name lookup (ADL). In this case, the powerful C++ compiler could solve this problem for us, and that without an error. Now this is certainly not interesting for a Delphi programmer, he might not even understand why I criticize this at all. And this might also explain why it is missing. Now one of the developers ran a grep- search over the current source code, and over the years 2.5 million calls of the method c_str() had accumulated in the source code. Should changes become necessary now, for example when migrating to a unixoid operating system, everyone can imagine the extent of the necessary work.
I would like to solve this problem for the future examples in this blog with the templates __ToVCL and __FromVCL. Again I use the predefined conditions for VCL or FMX, because only when using one of these frameworks the use of Delphi types seems to make sense. After that follow the two header files for Delphi string types <system.hpp> and C++ string types <string>.
For most data types you don't need international characters, so I use only a national variant for the implementation of the template and use the automatic conversion from UnicodeString to AnsiString in the Delphi class library. For the necessary type conversion I will use the template boost::lexical_cast, which can convert a string into any C++ type. This also works for data types defined by yourself, if you have implemented the stream operators.
#ifndef MyDelphiHelperH
#define MyDelphiHelperH
#if !defined BUILD_WITH_VCL && !defined BUILD_WITH_FMX
#error A framework variant must be selected for this application
#endif
#include <system.hpp>
#include <string>
#include <sstream>
#include <boost/lexical_cast.hpp>
template <typename ty>
inline String __ToVCL(ty const& val) {
std::ostringstream os;
os << val;
return os.str().c_str();
}
template <typename ty>
inline ty __FromVCL(String const& text) {
return boost::lexical_cast<ty>(AnsiString(text).c_str());
}
#endif
Since I always deal with Unicode strings on the surface of the Delphi frameworks, I have implemented this only for this type. But an extension to the Delphi type Ansistring should be very easy for everyone. I am not interested in a complete and working library, but in explaining techniques, problems and solutions.
Now the effort to carry the boost library on a mobile system like Android can be higher than the benefit. Or a compiler does not offer me the support of boost. Maybe there is an easy way, even boost must have found an approach. Therefore I change the above example as follows, and use the conditional translation again, if the target environment is Android.
#ifndef MyDelphiHelperH
#define MyDelphiHelperH
#if !defined BUILD_WITH_VCL && !defined BUILD_WITH_FMX
#error A framework variant must be selected for this application
#endif
#include <system.hpp>
#include <string>
#include <sstream>
#ifndef __ANDROID__
#include <boost/lexical_cast.hpp>
#endif
template <typename ty>
inline String __ToVCL(ty const& val) {
std::ostringstream os;
os << val;
return os.str().c_str();
}
template <typename ty>
inline ty __FromVCL(String const& text) {
#ifndef __ANDROID__
return boost::lexical_cast<ty>(AnsiString(text).c_str());
#else
std::istringstream ins(AnsiString(text).c_str());
ty val;
ins >> val;
return val;
#endif
}
#endif
Now this conversion is not effective, can also lead to errors if string types are used themselves. For these types I want to implement specializations. Normally the compiler creates a copy of the templates for each combination of type parameters and replaces the type parameters with the concrete types. This results in very robust and directly linked code that is faster than runtime decisions. Templates can also be specialized (define a different implementation for selected type parameters), which I want to take advantage of here.
There is also an inconsistency in the Delphi string types, because the indices here follow the standard behaviour on mobile operating systems and start with 0. Furthermore, the conversion from the 16 bit character set to the system type wchar_t, which uses 32 bit for all unixoidal ones, must be done here.
#ifndef MyDelphiHelperH
#define MyDelphiHelperH
#if !defined BUILD_WITH_VCL && !defined BUILD_WITH_FMX
#error A framework variant must be selected for this application
#endif
#include <system.hpp>
#include <string>
#include <sstream>
#ifndef __ANDROID__
#include <boost/lexical_cast.hpp>
#endif
template <typename ty>
inline String __ToVCL(ty const& val) {
std::ostringstream os;
os << val;
return os.str().c_str();
}
template <>
inline String __ToVCL<std::string>(std::string const& val) {
return val.c_str();
}
template <>
inline String __ToVCL<std::wstring>(std::wstring const& val) {
return val.c_str();
}
template <typename ty>
inline ty __FromVCL(String const& text) {
#ifndef __ANDROID__
return boost::lexical_cast<ty>(AnsiString(text).c_str());
#else
std::istringstream ins(AnsiString(text).c_str());
ty val;
ins >> val;
return val;
#endif
}
template <>
inline std::wstring __FromVCL<std::wstring>(String const& text) {
#ifdef __ANDROID__
// String indexes are 1-based in desktop platforms and 0-based in mobile platforms.
std::wstring strTemp2 = L"";
for(int i = 0; i < text.Length(); ++i) strTemp2.push_back(static_cast(text[i]));
return strTemp2. c_str();
#else
return text.c_str();
#endif
}
template <>
inline std::string __FromVCL<std::string>(String const& text) {
return AnsiString(text).c_str();
}
#endif
Now there may be problems with zero-terminated strings, since they are stored as const char*, unlike the international character fields with wchar_t. So I will implement a method with the same name __ToVCL, but it is not a template. For later use this is not important. And this approach frees me to keep the variants apart so that I can concentrate on content problems.
inline String __ToVCL(const char* val) {
return val;
}
With this I implemented the differences between the mobile operating systems and possible conversions from national to international character sets at a central location. Should there be adjustments, there will be no impact on the parts of my business application. I have the decisions in my hands again. This gives me the opportunity to create a test application that is the same for FMX and the VCL and also works with the mobile operating system Android as target platform.
#include "MainFormVCL.h"
#include "MyDelphiHelper.h"
#include <exception>
using namespace std;
void __fastcall TForm1::Button1Click(TObject *Sender) {
try {
int iWert = __FromVCL<int>(Edit1->Text);
iWert *= 5;
Edit2->Text = __ToVCL(iWert);
}
catch(exception& ex) {
ShowMessage(__ToVCL(ex.what()));
}
return;
}
Now I already hear the criticism that I have a lot of function calls here, moreover this source code creates unnecessary copies and therefore cannot be effective. But this is not true for modern C++ compilers anymore. I will explain this in a later blog post and also show you a concrete example. And the function calls just give me as a responsible programmer the possibility to make decisions, resolve inconsistencies and react to future changes. And this not distributed over the complete source code of my application, but at a central location. Should I have to rework them, it is much easier to search for __FromVCL or ::ToVCL in the source code.
Keine Kommentare
3 days
8 Participants
3 Days
8 Participants
In this seminar you will get a compact introduction to the latest version of C++Builder (currently 10.3.3). C++Builder is a cross-platform development tool that allows you to develop high-performance, native applications for the target platforms Windows (32bit, 64bit), Mac OS (32bit), iOS and Android.
5 Days
8 Participants
As the title of the course says, this course is about developing native applications for mobile devices (app) with C++Builder (currently 10.3.3).
2 Days
8 Participants
This course is designed for C++ developers who want to use the C++Builder and deepen their knowledge. In addition to using the components and the properties of the C++ language, it is especially important to combine both in a sensible way.
3 Days
8 Participants
This seminar is aimed at C++ programmers who want to extend access to databases in their applications. This seminar explains the main components of the C++ Builder for database access, but also shows how to integrate them into a clean application architecture.
3 Days
8 Participants
This seminar is aimed at C++ programmers who have gained experience with the database components of the C++ Builder and now want to develop scalable database applications. In this seminar the importance of ANSI C++ - interfaces between the business logic of an application and the database is shown.
3 Days
6 Participants
This course is aimed at software developers who want to learn the elements of the C programming language as a basis for a later entry into object-oriented programming with C++.
3 Days
8 Participants
This seminar is aimed at programmers who want to switch from C to C++. The most important extensions of the C++ language and the differences to C are discussed in detail.
3 Days
5 Participants
This seminar is aimed at programmers who want to switch from C to C++. The most important extensions of the C++ language and the differences to C are discussed in detail.
5 Days
8 Participants
We were often asked if we could do a training "with everything". A larger example, starting with an idea, developing an architecture from it, and then implementing it independently in C++. Just use the extensions without letting them determine our structure and architecture, and if possible, combine this with developing access for mobile applications.
5 Days
8 Participants
In this seminar you will learn which Open Source tools you can use for your daily work with the current C++ Builder 10.3.3. This includes solutions for documenting, metrics, code analysis and code management.
2 Days
6 Participants