Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/macmade/cfpp

C++ wrapper for CoreFoundation base classes.
https://github.com/macmade/cfpp

c-plus-plus corefoundation framework interoperability wrapper

Last synced: about 2 months ago
JSON representation

C++ wrapper for CoreFoundation base classes.

Awesome Lists containing this project

README

        

CoreFoundation++
================

[![Build Status](https://img.shields.io/github/actions/workflow/status/macmade/CFPP/ci-mac.yaml?label=macOS&logo=apple)](https://github.com/macmade/CFPP/actions/workflows/ci-mac.yaml)
[![Build Status](https://img.shields.io/github/actions/workflow/status/macmade/CFPP/ci-win.yaml?label=Windows&logo=windows)](https://github.com/macmade/CFPP/actions/workflows/ci-win.yaml)
[![Issues](http://img.shields.io/github/issues/macmade/CFPP.svg?logo=github)](https://github.com/macmade/CFPP/issues)
![Status](https://img.shields.io/badge/status-active-brightgreen.svg?logo=git)
![License](https://img.shields.io/badge/license-mit-brightgreen.svg?logo=open-source-initiative)
[![Contact](https://img.shields.io/badge/[email protected]?logo=twitter&style=social)](https://twitter.com/macmade)
[![Sponsor](https://img.shields.io/badge/sponsor-macmade-pink.svg?logo=github-sponsors&style=social)](https://github.com/sponsors/macmade)

### C++ wrapper for CoreFoundation base classes

Table of Contents
-----------------

1. [About CoreFoundation](#1)
2. [About CoreFoundation++](#2)
3. [Availability / Platforms](#3)
4. [Available / Wrapped classes](#4)
5. [Example](#5)
6. [Documentation](#6)
7. [To-Do](#7)
8. [License](#8)
9. [Repository Infos](#9)


About CoreFoundation
--------------------

Core Foundation (CF) is a C API available in Mac OS X & iOS:

>> Core Foundation is a framework that provides fundamental software services
>> useful to application services, application environments, and to applications
>> themselves. Core Foundation also provides abstractions for common data types,
>> facilitates internationalization with Unicode string storage, and offers
>> a suite of utilities such as plug-in support, XML property lists, URL
>> resource access, and preferences.

*Source: Core Foundation Framework Reference*

To learn more about CoreFoundation, please read the «[Core Foundation Design Concepts][1]» guide.

[1]: https://developer.apple.com/library/mac/documentation/corefoundation/Conceptual/CFDesignConcepts/CFDesignConcepts.html#//apple_ref/doc/uid/10000122i


About CoreFoundation++
----------------------

CoreFoundation++ (CFPP) is a C++ wrapper for Apple's CoreFoundation API.

CoreFoundation is a powerful C API which allows object-oriented style coding and reference counting memory management in C and provides a lot of useful classes.
CoreFoundation is intensively used in Apple's low-level frameworks, and is also used as a base for some classes of the Foundation (Objective-C) framework.

That being said, programming with CoreFoundation can sometimes be a real pain.
Even the simplest things takes several lines of code, which leads to obvious maintainability issues.
This is especially true when mixing CoreFoundation with C++, because you'll need to cast almost everything.

CoreFoundation++ uses the good parts of the C++ language to wrap the most used CoreFoundation classes, and to makes them easy to use, for instance by using operator overloading.


Availability / Platforms
------------------------

CoreFoundation++ is available for **MacOS X, iOS and Windows** (Windows builds assume the CoreFoundation DLLs are available), and can be compiled for **32 bits or 64 bits** systems.

A complete Xcode project is available, providing the following targets:

- A Mac OS X static library
- A Mac OS X dynamic library
- A Mac OS X framework
- An iOS static library

On Windows, a VisualStudio solution is provided with the following configurations:

- DLL - VC120_XP
- DLL - VC140_XP
- Static library - VC120_XP
- Static library - VC140_XP


Available / Wrapped classes
---------------------------

- CFType => CF::Type (abstract)
- CFBoolean => CF::Boolean
- CFNumber => CF::Number
- CFString => CF::String
- CFDate => CF::Date
- CFData => CF::Data
- CFMutableData => CF::Data
- CFURL => CF::URL
- CFArray => CF::Array
- CFMutableArray => CF::Array
- CFDictionary => CF::Dictionary
- CFMutableDictionary => CF::Dictionary
- CFUUIDRef => CF::UUID
- CFErrorRef => CF::Error
- CFReadStream => CF::ReadStream
- CFWriteStream => CF::WriteStream


Example
-------

Let's say you want to create a CFDictionary containing two keys, both CFString objects, the second one created from an external char pointer.
The value for the first key is a CFData object, created from an external data buffer, while the value for the second key is a CFArray object containing two values, a CFNumber object and a CFURL object, created from an external char pointer.

So let's assume the following:

extern const char * externalKey;
extern const char * externalByteBuffer;
extern size_t externalByteBufferLength;
extern const char * externalURL;

### Example with CoreFoundation++:

People used to the CoreFoundation API knows that creating dictionaries and arrays can be extremely painful.
But with CoreFoundation++, it's extremely easy:

{
CF::Number number = 42;
CF::URL url = externalURL;
CF::Array array;

/* Adds number and URL to the array */
array << number << url;

CF::Data data
(
( CF::Data::Byte * )externalByteBuffer,
( CFIndex )externalByteBufferLength
);

CF::Dictionary dictionary;

/* Adds the two key/value pairs in the dictionary */
dictionary << CF::Pair( "key-1", data );
dictionary << CF::Pair( externalKey, data );

/* Prints the dictionary */
std::cout << dictionary << std::endl;

/* Nothing more, memory is automatically managed... */
}

### Example with CoreFoundation:

For comparison, with the CoreFoundation C API, you will have write the following code:

{
/* First of all, we need a C array to store our dictionary keys */
CFStringRef keys[ 2 ];

/*
* Let's create the dictionary keys. First key is straightforward because
* of the CFSTR macro, while the second one is less...
*/
keys[ 0 ] = CFSTR( "key-1" );
keys[ 1 ] = CFStringCreateWithCString
(
( CFAllocatorRef )NULL,
externalKey,
kCFStringEncodingUTF8
);

/* Let's create the data object from the external byte buffer */
CFDataRef data = CFDataCreate
(
( CFAllocatorRef )NULL,
( const UInt8 * )externalByteBuffer,
( CFIndex )externalByteBufferLength
);

/*
* Now, let's create some number object. Note that we need a temporary
* variable, as we need to pass an address of a primitive type...
*/
int tempInt = 42;
CFNumberRef number = CFNumberCreate
(
( CFAllocatorRef )NULL,
kCFNumberSInt32Type,
&tempInt
);

/*
* Now, create an URL object. Note that we need to create a temporary
* CFString object
*/
CFStringRef tempStringURL = CFStringCreateWithCString
(
( CFAllocatorRef )NULL,
externalURL,
kCFStringEncodingUTF8
);
CFURLRef url = CFURLCreateWithString
(
( CFAllocatorRef )NULL,
tempStringURL,
NULL
);

/* Before creating the array, we need a C array to store the values */
CFTypeRef arrayValues[ 2 ];

arrayValues[ 0 ] = number;
arrayValues[ 1 ] = url;

/* Now we can create the array... */
CFArrayRef array = CFArrayCreate
(
( CFAllocatorRef )NULL,
( const void ** )arrayValues,
2,
&kCFTypeArrayCallBacks
);

/* Now, of course, we need a C array to store the dictionary values */
CFTypeRef values[ 2 ];

values[ 0 ] = data;
values[ 1 ] = array;

/* Finally, we can create our dictionary... */
CFDictionaryRef dictionary = CFDictionaryCreate
(
( CFAllocatorRef )NULL,
( const void ** )keys,
( const void ** )values,
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks
);

/* And of course, as we allocated objects, we need to release them... */
CFRelease( keys[ 1 ] );
CFRelease( data );
CFRelease( number );
CFRelease( tempStringURL );
CFRelease( url );
CFRelease( array );

/* That's it, prints the dictionary and release it */
CFShow( dictionary );
CFRelease( dictionary );
}


Documentation
-------------

### Memory management

CoreFoundation uses a reference counting memory management scheme.
All allocated, copied or retained objects must be explicitly released, when the ownership is no longer needed.

It's the same as in Objective-C (with MRC), with the difference that there's no autorelease mechanism.

CoreFoundation++ manages the memory for you, meaning you no longer have to call `CFRelease` explicitly.
All CF++ wrappers act as a shared pointer (`std::shared_ptr`).

They will automatically retain ownership of a CoreFoundation object, and several CF++ wrappers may own the same object.
The CoreFoundation object is destroyed and its memory deallocated when either of the following happens:

* The last remaining CF++ wrapper owning the object is destroyed
* The last remaining CF++ wrapper owning the object is assigned another CoreFoundation object via operator `=`.

For instance:

{
int x = 42
CFNumberRef cfNumber = CFNumberCreate
(
( CFAllocatorRef )NULL,
kCFNumberSInt32Type,
&x
);

CF::Number num1 = cfNumber;

/*
* CF::Number will retain the CFNumber object, so the one
* we allocated explicitly can be released safely
*/
CFRelease( cfNumber );

{
CF::Number num2

/* num1 and num2 owns the same CoreFoundation object */
num2 = num1:
}

/*
* End of scope - num2 is destroyed and will release
* the CFNumber object, which is still retained by num1
*/
}

/*
* End of scope - num1 is destroyed and will release
* the CFNumber object, which will be deallocated.
*/

### CF::Type

`CF::Type` is the base abstract class for all CF++ wrappers.

**Equality:**

Every CF++ wrapper can be compared using the `==` operator.
It will evaluate to `false` is the two operands are not of the same type. Otherwise, it will internally use `CFCompare` to test for equality:

CF::Number x;
CF::Boolean y;

if( x == y ) { /* ... */ }

**Casting:**

CF++ wrappers can be used transparently as CoreFoundation objects, thanks to the C++ cast operator.
It means you can use a CF++ wrapper object as argument to a CoreFoundation function:

CF::String s = "hello, world";

CFShow( s );

Besides, all CF++ wrappers can be created with CoreFoundation objects:

CFStringRef cfS = CFSTR( "hello, world" );
CF::String s( cfS );
/* Or also: */
CF::String s = cfS;

**Debug output:**

CF++ wrappers can be used with C++ `ostream` objects (like `std::cout`).
It will print the same output as a call to `CFShow`:

CF::Number x;

/* Both will produce the same output */
CFShow( x );
std::cout << x << std::endl;

### CF::Boolean

`CF::Boolean` is a wrapper for `CFBooleanRef`.

**Creation:**

`CF::Boolean` can be instantiated using another `CF::Boolean`, a `CFBooleanRef` or a `bool`:

CF::Boolean b1 = kCFBooleanTrue;
CF::Boolean b2 = b1;
CF::Boolean b3 = false;

**Mutation:**

`CF::Boolean` value can be set through the `=` operator:

CF::Boolean b1 = false;
b1 = true;

**Equality:**

`CF::Boolean` can be compared to another `CF::Boolean`, a `CFBooleanRef` or a `bool`:

CF::Boolean b1 = false;
CF::Boolean b2 = true;

if( b1 == b2 ) { /* ... */ }
if( b1 == kCFBooleanTrue ) { /* ... */ }
if( b1 == true ) { /* ... */ }

**Casting:**

`CF::Boolean` can be safely casted to a `bool` or `CFBooleanRef`:

CF::Boolean b1 = true;
CFBooleanRef cfBool = b1;
bool b = b1;

### CF::Number

`CF::Number` is a wrapper for `CFNumberRef`.

**Creation:**

`CF::Number` can be instantiated using another `CF::Number`, a `CFNumberRef` or an integral or floating point type:

CF::Number n1 = someCFNumberRefObject;
CF::Number n2 = n1;
CF::Number n3 = 42;
CF::Number n4 = 42.0f;

**Mutation:**

`CF::Number` value can be set/transformed through every operator:

CF::Number n1 = 42;
CF::Number n2 = 10;

n1--;
n2++;

n1 += n2;
n1 ^= n2
n2 += 10;
n1 &= 0xFF00;
n2 /= n1;
n1 = n1 | n2;
n1 = n1 >> 8;

/* etc... */

Note that you can determine is a `CF::Number` objects contains a floating point value using the `IsFloatType` method.

**Equality:**

`CF::Number` can be compared to another `CF::Number`, a `CFNumberRef` or a an integral or floating point type.
All comparison operators are available:

CF::Number n1 = 42;
CF::Number n2 = 10;

if( ( n1 != someCFNumberRefObject || n1 != n2 ) && n1 == 10 ) { /* ... */ }
if( n1 != 10.0f || n1 >= 20 || n2 < 40.0f ) { /* ... */ }
if( n1 || n2 ) { /* ... */ }

**Casting:**

`CF::Number` can be safely casted to a `CFNumberRef` or a an integral or floating point type:

CF::Number n1 = 42;
CFNumberRef cfNum = n1;
int i = n1;
long l = n1;
double d = n1;
int64_t i64 = n1;

### CF::String

CF::String is a wrapper for CFStringRef.

**Creation:**

`CF::String` can be instantiated using another CF::String, a CFStringRef, a `std::string` or an `char` pointer:

CF::String s1 = someCFStringRefObject;
CF::String s2 = s1;
CF::String s3 = std::string( "hello, world" );
CF::String s4 = "hello, world";

**Mutation:**

`CF::String` value can be set/transformed through the operator `=` and `+=` operator:

CF::String s = "hello"
s += ", world"
s = "hello, universe";

**Equality:**

`CF::String` can be compared to another `CF::String`, a `CFStringRef`, a `std::string` or a `char` pointer:

CF::String s1 = "hello, world";
CF::String s2 = "hello, universe";

if( s1 == someCFStringRefObject ) { /* ... */ }
if( s1 != s2 ) { /* ... */ }
if( s1 == std::string( "hello, universe" ) ) { /* ... */ }
if( s1 != "hello, universe" ) { /* ... */ }

Casting:

`CF::String` can be safely casted to a `CFStringRef`, a `std::string` or a `char` pointer:

CF::String s1 = "hello, world";
CFStringRef cfS = s1;
std::string s = s1;
char * cp = s1;

**Character access:**

Individual characters can be accessed through the `[]`operator:

CF::String s1 = "hello, world";
char c1 = s1[ 1 ] /* 'e' */;
char c2 = s1[ -1 ] /* 'd' */;

### CF::Array

`CF::Array` is a wrapper for `CFArrayRef`.

**Creation:**

`CF::Array` can be instantiated using another `CF::Array`, or a `CFArrayRef`.
All `CF::Array` objects are created as mutable, and an initial capacity can be passed to the constructor:

CF::Array a1 = someCFArrayRefObject;
CF::Array a2 = a1;
CF::Array a3( 100 );

**Mutation:**

`CFTypeRef` or `CF:Type` objects can be added to an array using the `<<` operator:

CF::Array a;
CF::Number n = 42;
CF::String s = "hello, world";

a << someCFTypeRefObject;
a << n;
a << s;

/* Or simply: */
a << someCFTypeRefObject << n << s;

An other `CF:Array` can be appended using the `+=` operator:

CF::Array a1;
CF::Array a2;
CF::Number n = 42;
CF::String s = "hello, world";

a1 << n;
a2 << s;
a1 += a2;

**Element access:**

Values can be retrieved using the `[]` operator:

CF::Array a;
CF::Number n1 = 42;
CF::String s1 = "hello, world";

a << someCFTypeRefObject << n1 << s1;

CFTypeRef cfObject = a[ 0 ];
CF::Number n2 = a[ 1 ];
CF::String s2 = a[ 2 ];

**Casting:**

`CF::Array` can be safely casted to a `CFArrayRef`:

CF::Array a( 100 );
CFArrayRef cfA = a;

**Other methods:**

Other `CFArrayRef` methods are available, like `GetCount`, `RemoveValueAtIndex`, `ExchangeValuesAtIndices`, etc.

### CF::Dictionary

`CF::Dictionary` is a wrapper for `CFDictionaryRef`.

**Creation:**

`CF::Dictionary` can be instantiated using another `CF::Dictionary`, or a `CFDictionaryRef`.
All `CF::Dictionary` objects are created as mutable, and an initial capacity can be passed to the constructor:

CF::Dictionary d1 = someCFDictionaryRefObject;
CF::Dictionary d2 = d1;
CF::Dictionary d3( 100 );

**Mutation:**

A key/value pair can be added to a dictionary using the `+=` operator.
To create a key/value pair, use the `CF::Pair` object, which can be constructed with `CFTypeRef` or `CF:Type` objects.
Note that `std::string` or `char` pointers can be safely used as keys:

CF::Dictionary d;
CF::String s = "key";
CF::Number n = 42;

d += CF::Pair( someCFTypeRefObject, someOtherCFTypeRefObject );
d += CF::Pair( s, n );
d += CF::Pair( std::string( "key" ), n );
d += CF::Pair( "key", n );

A key can be replaced using the `<<` operator:

CF::Dictionary d;
CF::Number n1 = 42;
CF::Number n2 = 43;

d += CF::Pair( s, n1 );
d << CF::Pair( s, n2 );

**Casting:**

`CF::Dictionary` can be safely casted to a `CFDictionaryRef`:

CF::Dictionary d( 100 );
CFDictionaryRef cfD = d;

**Other methods:**

Other `CFDictionaryRef` methods are available, like `GetCount`, `ContainsKey`, `RemoveValue`, etc.

### CF::Date

`CF::Date` is a wrapper for `CFDateRef`.

**Creation:**

`CF::Date` can be instantiated using another `CF::Date`, a `CFDateRef` or a `CFAbsoluteTime`.
When a `CF::Date` object is created without argument, it defaults to the current date:

CF::Date d1;
CF::Date d2 = someCFDateRefObject;
CF::Date d3 = d1;
CF::Date d4 = 100;

**Mutation:**

`CF::Date` value can be set/transformed through many operators:

CF::Date d1;
CF::Date d2;

d1++;
d2--;

d1 += d2;
d2 = d1 + d2;

**Equality:**

`CF::Date` can be compared to another `CF::Date`, a `CFDateRef` or a `CFAbsoluteTime`.
Almost all comparison operators are available:

CF::Date d1;
CF::Date d2;

if( d1 == someCFDateRefObject ) { /* ... */ }
if( d1 != d2 ) { /* ... */ }
if( d1 > 100 ) { /* ... */ }
if( d1 <= d2 ) { /* ... */ }

**Casting:**

`CF::Date` can be safely casted to a `CFDateRef` or `CFAbsoluteTime`:

CF::Date d;
CFDateRef cfD = d;
CFAbsoluteTime t = d;

### CF::Data

`CF::Data` is a wrapper for `CFDataRef`.

**Creation:**

`CF::Data` can be instantiated using another `CF::Data`, a `CFDataRef`, a `CFStringRef`, a `std::string` or a `CF::Data::Byte` pointer:

const char * cp = "hello, world";
CF::Data d1 = someCFDataRefObject;
CF::Data d2 = someCFStringRefObject;
CF::Data d3 = d1;
CF::Data d4 = std::string( "hello, world" );
CF::Data d5( ( CF::Data::Byte * )cp, 12 );

**Mutation:**

Data can be appended using the `+=` operator.
The following types can be appended: `CF::Data::Byte`, `CFStringRef`, `CFDataRef`and `std::string`:

CF::Date d;

d += 42;
d += someCFtringRefObject;
d += someCFDataRefObject;
d += std::string( "hello, world" );

To append a `CF::Data::Byte` pointer, une the `AppendBytes` method.

**Element access:**

Single bytes can be retrieved as `CF::Data::Byte` using the `[]` operator:

CF::Date d( std::string( "hello, world" ) );
CF::Date::byte b = d[ 2 ];

**Casting:**

`CF::Data` can be safely casted to a `CFDataRef`, `std::string` and `CF::Data::Byte` pointer:

CF::Data d;
CFDateRef cfD = d;
std::string s = d;
CF::Data::Byte * bp = d;

**Other methods:**

Other `CFDataRef` methods are available, like `GetLength`, `GetMutableBytePtr`, `ReplaceBytes`, etc.

### CF::URL

`CF::URL` is a wrapper for `CFURLRef`.

**Creation:**

`CF::URL` can be instantiated using another `CF::URL`, a `CFURLRef`, a `CFStringRef`, or a `std::string`:

CF::URL url1 = someCFURLRefObject;
CF::URL url2 = someCFStringRefObject;
CF::URL url3 = url1;
CF::URL url4 = std::string( "http://www.example.org/" );

**Equality:**

CF::URL can be compared to another CF::URL, a CFURLRef, a CFStringRef, or a std::string:

CF::URL url1( "http://www.example.org/" );
CF::URL url2( "http://www.xs-labs.com/" );

if( url1 == url2 ) { /* ... */ }
if( url1 != someCFURLRefObject ) { /* ... */ }
if( url1 == someCFStringRefObject ) { /* ... */ }
if( url1 != std::string( "http://www.xs-labs.com/" ) ) { /* ... */ }

**Mutation:**

Path components can be appended to a `CF::URL` using the `/=` operator:

CF::URL url1( "http://www.example.org/" );

url /= "path";
url /= "to";
url /= "some";
url /= "page";

**Parts access:**

Individual URL parts can be accessed as `std::string` using the `[]` operator, specifying a `CF::URL::Part`, or through regular methods:

CF::URL url( "http://www.example.org/" );

std::string scheme = url[ CF::URL::PartScheme ];
std::string host = url.GetHostName();

**Casting:**

`CF::URL` can be safely casted to a `CFURLRef`, `CFStringRef`, or `std::string`:

CF::URL url( "http://www.example.org/" );
CFURLRef cfUrl = url;
CFStringRef cfS = url;
std::string s = url;


To-Do
-----

The following classes will be implemented soon:

- CFBundle
- CFDateFormatter
- CFNotificationCenter
- CFNull
- CFNumberFormatter
- CFRunLoop
- CFRunLoopSource
- CFRunLoopTimer
- CFSocket
- CFSet
- CFMutableSet


License
-------

CoreFoundation++ is released under the terms of the MIT License.


Repository Infos
----------------

Owner: Jean-David Gadina - XS-Labs
Web: www.xs-labs.com
Blog: www.noxeos.com
Twitter: @macmade
GitHub: github.com/macmade
LinkedIn: ch.linkedin.com/in/macmade/
StackOverflow: stackoverflow.com/users/182676/macmade