Meet The Reimagined C++
General
1. Flawless Readability — The APIs are robust and are designed to make the code self-documenting. For example, cumbersome pranteces are removed in type-casting, and can be read more like an English sentence:
myObject.AS(BaseClass)->baseClassMethod();
Or, object serialization reads like:
serializer->object("MyClass")->attribute("attrib", value)->endObject();
and, here is also a little treat for old C pointers:
if(IS_NULL(cPointer)) { /* do something */ }
2. Platform-Decontaminated Headers — KFoundation is self-sufficient. Headers do not include from any platform-specific headers, or from C++ standard libraries.
3. All sizes based on octet, fixed on every platform —Everything is measured in octets. The number of octets in all types, including nummeric and string types are fixed on all platforms.
4. Compatible — Designed to work with any standard C++ 98 and above compiler, because not all compilers implement the entire new standard specifications.
5. Exceptions with Human- and Machine-readable stack trace — KFoundation exceptions save the stack trace once thrown and present them in a format readable by both man and machine.
Memory Management
6. Not Only Better, but Faster — The whole concept of pointer is replaced with a structured reference, that results in: Less creation time. Faster book keeping and reference counting. Safety check when dereferencing comes at almost no additional cost. Time measure is in miliseconds per 1,000,000 repeatations.
7. Hassle-free Assignment — Short and easy name. One kind. Creation using assignment operator.
/* OLD */ auto_ptr<MyClass> myObject(new MyClass());
/* OLD */ unique_ptr<MyClass> myObject(new MyClass());
/* OLD */ shared_ptr<MyClass> myObject(new MyClass());
/* NEW */ Ref<MyClass> myObject = new MyClass();
8. Automatic Management — Reference-counted. No need to delete. No need for deconstructors.
Before:
void myFunction() {
MyClass* ptr = new MyClass();
/* ... */
delete ptr;
}
class OldClass {
MyClass* field;
OldClass() : field(new MyClass()) { }
~OldClass() {
delete field; // Segmentation fault if field is used
// or deleted by another class
}
};
After:
void myFunction() {
Ref<MyClass> ptr = new MyClass();
/* ... */
}
class NewClass {
Ref<MyClass> field; // Automatically deleted when no longer needed.
NewClass() : field(new MyClass()) { }
};
9. Stratigically Small — Unlike shared_ptr<T> which is 16 bytes big on 64-bit platforms, Ref<T> is 8 bytes and fits in a single register of most modern machines, therefore it is much faster to pass around as function argument.
10. Streamlined type-casting and type-checking
/* OLD */ if(!dynamic_cast<MyBaseClass*>(myPtr)) { ... }
/* OLD */ dynamic_cast<MyBaseClass*>(myPtr)->myBaseClassMethod();
/* NEW */ if(myRef.ISA(MyBaseClass)) { ... }
/* NEW */ myPtr.AS(MyBaseClass)->myBaseMethod();
11. Globally Safe — GUR is the first and only reference or pointer type made to be shared across distributed nodes. It is safe because it does not reveal the real memory address, and detection of bogous values is built in its design.
12. Extensible Memory Management — The memory referenced by Ref<T> can be managed by any of seveal coexisting user-defined memory managers. If not specified, Ref<T> uses the default reference-counted memory manager. But any static, object-pool, shared memory, or any other kind of manager can be defined and used. Ref<T> acts as the single front-end to all these managers, rather than having a different pointer class for each type of mangement as in C++ Standard Libraries.
13. Proactive Fault Detection —Ref<T> detects segmentation fault before it happens, and throws an exception instead. All thrown excpetions can print stack trace which makes cumbersome debugging of such erros much easier and faster. Programs can gain improved stability by catching and handling these exceptions.
/* OLD */ shared_ptr<MyClass> ptr(NULL);
/* OLD */ ptr->method(); // --> Segmentation fault!
/* NEW */ Ref<MyClass> ref = NULL;
/* NEW */ ref->method(); // --> NullPointerException with stack trace.
/* OLD */ shared_ptr<MyClass> ptr(/*invalid value*/)
/* OLD */ ptr->method(); // --> Unpredictable behavior.
/* NEW */ Ref<MyClass> ref = /*invalid value*/;
/* NEW */ ref->method(); // --> InvalidPointerException with stack trace.
14. Reference-to-Constant
/* OLD */ shared_ptr<MyClass> ptr(new MyClass());
/* OLD */ shared_ptr<const MyClass> ptrConst = ptr; // Compiler error
/* NEW */ Ref<MyClass> ref = new MyClass();
/* NEW */ RefConst<MyClass> refConst = ref; // OK
UTF-8 String
15. Choice of UTF-8
-
Most compact memory representation.
-
Fast streaming
-
No need for byte-order conversion over network
-
No need for encoding conversion in most cases, because UTF-8 is the most widely international encoding standard.
-
16. String Literal — Works without precompiler or compiler modification. Wraps around the original memory in program text area, resulting in faster creation time, and less memory consumption.
Ref<UString> myStr = K"I am a UString!";
17. UStringWindow — Wraps around the memory of the original string, making substrings faster and more memory-efficient.
Ref<UString> mySubStr = new UStringWindow(myStr, 7, 14);
18. UTF-8 Characters — UChar class uses UTF-8 internally to make it faster to read and search in strings without conversion. It supports opeations like toLowerCase() or isNumeric() for entire Unicode range.
19. UCharIterator — A fast and easy way to traverse a UString. It is a subclass of UChar.
for(UCharIterator ch = myStr->getUCharIterator(); ch.hasMore(); ch.next()) {
System::OUT << "Unicode for '" << ch << "' is: " << ch.toWChar() << OVER;
}
20. OctetIterator — To iterate over octets in a string.
for(OctetIterator o = myStr->getOctetIterator(); o.hasMore(); o.next()) { ... }
21. UString Concatenates With Everything
Ref<UString> myStr = K"Concatanates with number " + 42 + K", bool " + true
+ ", C string, and everything else " + *myRef;
22. StringPrintWriter — For fast string creation. UString is immutable, which means every operation results in new UString. StringPrtinWriter resuses internal expandable buffer.
Containers
23. Reference Containers — RefArray and RefDictionary are container types for KFoundation references. They make sure memory management rule applies for all elements. RefArray offers all standard vector operations.
24. Type Wrappers — To support primitive types within KFoundation reference-based life-cycle, there are type wrapper classes provided for boolean and numerical values: Bool, Double, Int, LongInt.
25. KFWrapper — For everything else, KFWrapper class wraps around them and makes them compatible with KFoundation reference-based system.
26. Scalar Array — Array and NumericVector are Ref-compatible container classes that can contain any scalar type. NumericVector defines mathematical operators.
I/O Streams
27. Easily Expandable — The interface design of IO Stream base classes InputStream and OutputStream are minimal. Therefore more variety of the can be created more easily.
28. PrintWriter — One writer, for all output streams.
Ref<FileOutputStream> file = new FileOutputStream(myFilePath);
PrintWriter(file) << "A \"text\", and a number: " << 42 << OVER;
29. Parser — One for all input streams. Customizable.
Ref<StreamParser> parser = new StreamParser(System::IN);
parser->skipSpaces();
double value;
int nReadOctets = parser->readNumber(value);
if(nReadOctets == 0) {
LOG << "Could not read number." << OVER;
} else {
LOG << "The number is: " << value << OVER;
}
30. Standard Outputs — System class provides access to standard output via PrintWriter interface.
const StaticRef<PrintWriter> System::OUT;
const StaticRef<PrintWriter> System::ERR;
Standard input is a InputStream:
const StaticRef<InputStream> System::IN;
31. Internet IO — Ready-made InternetInputStream and InternetOutputStream are provided to stream back and forth over the Internet.
32. Path — One stop to all path information: segments, extension, size, time stamps, permisions. Computes MD5 checksum. Memory efficient implmentation: wraps around the given string, and uses UStringWindow whenever possibe.
Ref<Path> path = new Path(K"../path/to/my/file.txt");
if(!path->exists()
&& !path->isDirectory()
&& path->hasReadPermission())
{
Path::MD5 md5 = path->computeMd5();
}
33. File IO Synchronization — FileOutputStream and FileInputStream provide APIs for file lock.
Logger
34. Short Input, Ritch Output — 3-letter invocation. Prints file name, function name, and time stamp.
LOG << "Hello Logger!" << OVER;
Output:
20:56:20.150 [testLogger@TestCases.cpp:27] Hello Logger!
35. Multi-level — Five levels, from highest: LOG_ERR, LOG_WRN, LOG_L1, LOG_L2, LOG_L3. LOG is shortcut for LOG_L3. The set output level filter:
System::getLogger()->setLevel(Logger::WRN);
36. Multi-channel — The logger output can be configured to be set to multiple outputs (file, console, etc.).
37. Wanishing Macros — Even when the output of a log statement is filtered out, it consumes some CPU time. A set of macros will wanish the log statement all together when compiled for release.
DLOG("This will be computed only on debug: " << timer.get());
38. Writes to OS-Wide Logger — Logger can also be configured to send its output to the centralized logger facility provided by the operating system.
System::getLogger()->setSystemLoggerOutput(ON);
39. Thread-Safe — Normally when two threads write on the same output, the outputs can be mixed up and unreadable. Logger uses per-thread buffer and shared mutex to make sure each output is written separatedly.
Object I/O
40. (De)serialization for All — Every object can be easily made serializable by implementing the serialize() method from SerializingStreamer interface. Likewise, every object can be made to deserialized from stream by implementing deserialize() from StreamDeserializer interface.
41. One API to All Marked-up Languages — One implementation of SerializingStreamer::serialize() can output in any language, like KFOR, XML, and JSON. Likewise, one implementation of StreamDeserializer::deserialize() can read from any of supported language.
42. Intuitive APIs — Assume the following class:
class MyClass : public KFObject, public SerializingStreamer, public StreamDeserializer {
private: kf_int32_t _intField;
private: Ref<UString> _stringField;
public: void serialize(Ref<ObjectSerializer>) const;
public: void deserialize(Ref<ObjectToken>);
};
This is how serializer works:
void MyClass::serialize(Ref<ObjectSerializer> serializer) const {
serializer->object(K"MyClass")
->attribute(K"intField", _intField)
->member(K"stringField")->object(*_stringField)
->endObject();
}
This is how deserializer works:
void MyClass::deserialize(Ref<ObjectToken> token) {
token->validateClassName(K"MyClass");
_intField = token->next()->asAttribute()->validateName(K"intField")
->getInt32Value();
_stringField->deserialize(token->next()->asObject());
}
43. Introducing KFOR — None of available markedup languages encode object in a type safe way. XML misses field names, and JSON and YAML miss class names. Therefore, KFoundation introduces its own markedup format, KFOR (KFoundation FOrmat). Also, KFOR pretty representation is more compact than all others.
XML:
<MyClass intField="42">
<UString value="Hello" />
</MyClass>
JSON:
{
"intField": 42,
"stringField": {
"value": "Hello"
}
}
KFOR:
MyClass[intField: 42,
stringField: UString[value:"Hello"]]
44. Even More Languages — APIs for ObejctSerializer and ObjectStreamReader are easily extensible to support even more markedup formats.
45. Memory Efficient Implementation — ObjectSerializer does not have an internal stream buffer. It keeps merely enough information to validate its input, and discards them as soon as they are no longer needed. ObjectStreamReader also does not have internal buffer. It merely keeps enough information to validate its input. Thanks to KFoundation's automatic memory management, each read token is immidiately discarded when no longer needed.
46. For RESTful applications — KFoundation with its net I/O and serialization and deserialization facilities gives the programmers everything they need to create a fast C++ restful server program.
47. For Easy Debugging — Implementing SerializingStreamer, you get an immidate dump of object contents as easy as this:
LOG << *myObject << OVER;
Multi-Threading
48. Thead Class — The thread class does not include any non-KFoundation headers, works on every platform the same.
class MyThread : public Thread {
public: void run() {
LOG << K"Message from thread \"" << getName() << "\"" << OVER;
}
};
Ref<MyThread> thread = new MyThread("HappyThread");
thread->start();
49. Mutex and Condition — Intutive cross-platform APIs are provided for synchronization between threads.
Mutex::lock();
Mutex::unlock();
Condition::block();
Condition::block(kf_int64_t timeout);
Condition::release();
Condition::releaseAll();
50. Synchronized Block — KF_SYNCHRONIZED macro emulates Java-like synchronized block to protect a critical region.
KF_SYNCHRONIZED(myMutex, {
/* Critical Region */
})
HPC Tools
51. Iterate Multiple Dimensions in One Loop — This is a true saviour for HPC programmers.
for(RangeIterator i(Tuple3D(1000, 300, 400)); i.hasMore(); i.next()) {
Tuple& thisPoint = i; // Because RangeIterator is a subclass of Tuple.
}
52. Stensil Computation in Two Loops — The next level of cool.
ProximityIterator j(/* neighborhood size = */ 1);
for(RangeIterator i(Tuple3D(1000, 300, 400)); i.hasMore(); i.next()) {
for(j.centerAt(i); j.hasMore(); j.next()) {
Tuple& thisPoint = j;
}
}
53. Helper for Space Scheduling and Synchronization — Compute how a space is divided between any given set of nodes, and which cells are shared between those nodes.
/* The global range */
Range globalRange(Tuple3D(0, 0, 0), Tuple3D(1000, 1000, 1000));
/* The subrange for this node */
Range localRange = globalRange.divide(Tuple3D(4, 4, 4), Tuple3D(1, 2, 3));
/* The data you should store using a stensil of diameter 1 */
Range localDataRange = localRange.grow(1);
/* Which part of your local computation should be exchanged with a given neighbor */
Range exchangeRange = dataRange.intersectWith(neighborRange);
54. Direction Iterator — Iterate directions in n-Dimensional space.
for(DirectionIterator i(/* dimensions = */ 4); i.hasMore(); i.next()) {
Direction& thisDirection = i;
}