Merge branch 'master' into dev_loc

# Conflicts:
#	coreapi/error_info.c
This commit is contained in:
Sandrine Avakian 2017-03-27 15:36:31 +02:00
commit 281cabcf4f
24 changed files with 368 additions and 155 deletions

View file

@ -32,6 +32,13 @@
if(NOT LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
include("${CMAKE_CURRENT_LIST_DIR}/LinphoneTargets.cmake")
endif()
if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
include("${EP_ms2_CONFIG_DIR}/Mediastreamer2Config.cmake")
include("${EP_bellesip_CONFIG_DIR}/BelleSIPConfig.cmake")
else()
find_package(Mediastreamer2 REQUIRED)
find_package(BelleSIP REQUIRED)
endif()
if(@ENABLE_SHARED@)
set(LINPHONE_TARGETNAME linphone)

View file

@ -22,22 +22,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneErrorInfo);
#define STRING_RESET(field) if (field) ms_free(field); field = NULL;
static void linphone_error_info_reset(LinphoneErrorInfo *ei);
static void error_info_destroy(LinphoneErrorInfo *ei){
if (ei->protocol) ms_free(ei->protocol);
if (ei->phrase) ms_free(ei->phrase);
if (ei->warnings) ms_free(ei->phrase);
if (ei->full_string) ms_free(ei->full_string);
linphone_error_info_reset(ei);
}
static void error_info_clone(LinphoneErrorInfo *ei, const LinphoneErrorInfo *other){
ei->protocol = ms_strdup_safe(other->protocol);
ei->phrase = ms_strdup_safe(other->phrase);
ei->warnings = ms_strdup_safe(other->phrase);
ei->full_string = ms_strdup_safe(other->full_string);
ei->protocol = bctbx_strdup(other->protocol);
ei->phrase = bctbx_strdup(other->phrase);
ei->warnings = bctbx_strdup(other->warnings);
ei->full_string = bctbx_strdup(other->full_string);
}
BELLE_SIP_INSTANCIATE_VPTR(LinphoneErrorInfo, belle_sip_object_t,
@ -155,7 +152,7 @@ int linphone_reason_to_error_code(LinphoneReason reason) {
return 400;
}
void linphone_error_info_reset(LinphoneErrorInfo *ei){
static void linphone_error_info_reset(LinphoneErrorInfo *ei){
ei->reason = LinphoneReasonNone;
STRING_RESET(ei->protocol);
STRING_RESET(ei->phrase);
@ -170,11 +167,11 @@ void linphone_error_info_reset(LinphoneErrorInfo *ei){
void linphone_error_info_from_sal(LinphoneErrorInfo *ei, const SalErrorInfo *sei){
ei->reason = linphone_reason_from_sal(sei->reason);
ei->phrase = ms_strdup_safe(sei->status_string);
ei->full_string = ms_strdup_safe(sei->full_string);
ei->warnings = ms_strdup_safe(sei->warnings);
ei->phrase = bctbx_strdup(sei->status_string);
ei->full_string = bctbx_strdup(sei->full_string);
ei->warnings = bctbx_strdup(sei->warnings);
ei->protocol_code = sei->protocol_code;
ei->protocol = ms_strdup_safe(sei->protocol);
ei->protocol = bctbx_strdup(sei->protocol);
}
/* If a reason header is provided (in reason_ei), then create a sub LinphoneErrorInfo attached to the first one, unless the reason header

View file

@ -39,17 +39,40 @@ typedef belle_sip_object_t_vptr_t LinphoneFactory_vptr_t;
struct _LinphoneFactory {
belle_sip_object_t base;
/*these are the directories set by the application*/
char *top_resources_dir;
char *data_resources_dir;
char *sound_resources_dir;
char *ring_resources_dir;
char *image_resources_dir;
char *msplugins_dir;
/*these are the cached result computed from directories set by the application*/
char *cached_data_resources_dir;
char *cached_sound_resources_dir;
char *cached_ring_resources_dir;
char *cached_image_resources_dir;
char *cached_msplugins_dir;
};
static void linphone_factory_uninit(LinphoneFactory *obj){
STRING_RESET(obj->top_resources_dir);
STRING_RESET(obj->data_resources_dir);
STRING_RESET(obj->sound_resources_dir);
STRING_RESET(obj->ring_resources_dir);
STRING_RESET(obj->image_resources_dir);
STRING_RESET(obj->msplugins_dir);
STRING_RESET(obj->cached_data_resources_dir);
STRING_RESET(obj->cached_sound_resources_dir);
STRING_RESET(obj->cached_ring_resources_dir);
STRING_RESET(obj->cached_image_resources_dir);
STRING_RESET(obj->cached_msplugins_dir);
}
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFactory);
BELLE_SIP_INSTANCIATE_VPTR(LinphoneFactory, belle_sip_object_t,
NULL, // destroy
linphone_factory_uninit, // destroy
NULL, // clone
NULL, // Marshall
FALSE
@ -64,9 +87,16 @@ static void _linphone_factory_destroying_cb(void) {
}
}
static LinphoneFactory *linphone_factory_new(void){
LinphoneFactory *factory = belle_sip_object_new(LinphoneFactory);
factory->top_resources_dir = bctbx_strdup(PACKAGE_DATA_DIR);
return factory;
}
LinphoneFactory *linphone_factory_get(void) {
if (_factory == NULL) {
_factory = belle_sip_object_new(LinphoneFactory);
_factory = linphone_factory_new();
atexit(_linphone_factory_destroying_cb);
}
return _factory;
@ -107,67 +137,74 @@ LinphoneVcard *linphone_factory_create_vcard(LinphoneFactory *factory) {
return _linphone_vcard_new();
}
char * linphone_factory_get_top_resources_dir(const LinphoneFactory *factory) {
if (factory->top_resources_dir) return bctbx_strdup(factory->top_resources_dir);
return bctbx_strdup(PACKAGE_DATA_DIR);
const char * linphone_factory_get_top_resources_dir(const LinphoneFactory *factory) {
return factory->top_resources_dir;
}
void linphone_factory_set_top_resources_dir(LinphoneFactory *factory, const char *path) {
if (factory->top_resources_dir) bctbx_free(factory->top_resources_dir);
factory->top_resources_dir = bctbx_strdup(path);
STRING_SET(factory->top_resources_dir, path);
}
char * linphone_factory_get_data_resources_dir(const LinphoneFactory *factory) {
if (factory->data_resources_dir) return bctbx_strdup(factory->data_resources_dir);
if (factory->top_resources_dir) return bctbx_strdup_printf("%s/linphone", factory->top_resources_dir);
return bctbx_strdup_printf("%s/linphone", PACKAGE_DATA_DIR);
const char * linphone_factory_get_data_resources_dir(LinphoneFactory *factory) {
if (factory->data_resources_dir) return factory->data_resources_dir;
if (factory->top_resources_dir){
STRING_TRANSFER(factory->cached_data_resources_dir, bctbx_strdup_printf("%s/linphone", factory->top_resources_dir));
}else{
STRING_TRANSFER(factory->cached_data_resources_dir, bctbx_strdup_printf("%s/linphone", PACKAGE_DATA_DIR));
}
return factory->cached_data_resources_dir;
}
void linphone_factory_set_data_resources_dir(LinphoneFactory *factory, const char *path) {
if (factory->data_resources_dir) bctbx_free(factory->data_resources_dir);
factory->data_resources_dir = bctbx_strdup(path);
STRING_SET(factory->data_resources_dir, path);
}
char * linphone_factory_get_sound_resources_dir(const LinphoneFactory *factory) {
if (factory->sound_resources_dir) return bctbx_strdup(factory->sound_resources_dir);
if (factory->top_resources_dir) return bctbx_strdup_printf("%s/sounds/linphone", factory->top_resources_dir);
return bctbx_strdup(PACKAGE_SOUND_DIR);
const char * linphone_factory_get_sound_resources_dir(LinphoneFactory *factory) {
if (factory->sound_resources_dir) return factory->sound_resources_dir;
if (factory->top_resources_dir){
STRING_TRANSFER(factory->cached_sound_resources_dir, bctbx_strdup_printf("%s/sounds/linphone", factory->top_resources_dir));
return factory->cached_sound_resources_dir;
}
return PACKAGE_SOUND_DIR;
}
void linphone_factory_set_sound_resources_dir(LinphoneFactory *factory, const char *path) {
if (factory->sound_resources_dir) bctbx_free(factory->sound_resources_dir);
factory->sound_resources_dir = bctbx_strdup(path);
STRING_SET(factory->sound_resources_dir, path);
}
char * linphone_factory_get_ring_resources_dir(const LinphoneFactory *factory) {
if (factory->ring_resources_dir) return bctbx_strdup(factory->ring_resources_dir);
if (factory->sound_resources_dir) return bctbx_strdup_printf("%s/rings", factory->sound_resources_dir);
return bctbx_strdup(PACKAGE_RING_DIR);
const char * linphone_factory_get_ring_resources_dir(LinphoneFactory *factory) {
if (factory->ring_resources_dir) return factory->ring_resources_dir;
if (factory->sound_resources_dir){
STRING_TRANSFER(factory->cached_sound_resources_dir, bctbx_strdup_printf("%s/rings", factory->sound_resources_dir));
return factory->cached_sound_resources_dir;
}
return PACKAGE_RING_DIR;
}
void linphone_factory_set_ring_resources_dir(LinphoneFactory *factory, const char *path) {
if (factory->ring_resources_dir) bctbx_free(factory->ring_resources_dir);
factory->ring_resources_dir = bctbx_strdup(path);
STRING_SET(factory->ring_resources_dir, path);
}
char * linphone_factory_get_image_resources_dir(const LinphoneFactory *factory) {
if (factory->image_resources_dir) return bctbx_strdup(factory->image_resources_dir);
if (factory->top_resources_dir) return bctbx_strdup_printf("%s/images", factory->top_resources_dir);
return bctbx_strdup_printf("%s/images", PACKAGE_DATA_DIR);
const char * linphone_factory_get_image_resources_dir(LinphoneFactory *factory) {
if (factory->image_resources_dir) return factory->image_resources_dir;
if (factory->top_resources_dir) {
STRING_TRANSFER(factory->cached_image_resources_dir, bctbx_strdup_printf("%s/images", factory->top_resources_dir));
}else{
STRING_TRANSFER(factory->cached_image_resources_dir, bctbx_strdup_printf("%s/images", PACKAGE_DATA_DIR));
}
return factory->cached_image_resources_dir;
}
void linphone_factory_set_image_resources_dir(LinphoneFactory *factory, const char *path) {
if (factory->image_resources_dir) bctbx_free(factory->image_resources_dir);
factory->image_resources_dir = bctbx_strdup(path);
STRING_SET(factory->image_resources_dir, path);
}
char * linphone_factory_get_msplugins_dir(const LinphoneFactory *factory) {
return bctbx_strdup(factory->msplugins_dir);
const char * linphone_factory_get_msplugins_dir(LinphoneFactory *factory) {
return factory->msplugins_dir;
}
void linphone_factory_set_msplugins_dir(LinphoneFactory *factory, const char *path) {
if (factory->msplugins_dir) bctbx_free(factory->msplugins_dir);
factory->msplugins_dir = bctbx_strdup(path);
STRING_SET(factory->msplugins_dir, path);
}
LinphoneErrorInfo *linphone_factory_create_error_info(void){

View file

@ -20,7 +20,7 @@
#
############################################################################
if (ENABLE_DOC)
if (ENABLE_DOC OR CXX_WRAPPER)
find_package(Doxygen)
if(DOXYGEN_FOUND)
if(DOXYGEN_DOT_FOUND)
@ -39,7 +39,11 @@ if (ENABLE_DOC)
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/doc/html" "${CMAKE_CURRENT_BINARY_DIR}/doc/xml"
DESTINATION "${CMAKE_INSTALL_DATADIR}/doc/linphone-${LINPHONE_VERSION}")
else()
message(WARNING "The dot program is needed to generate the linphone documentation. You can get it from http://www.graphviz.org/.")
if (CXX_WRAPPER)
message(FATAL_ERROR "The dot program is needed to generate the linphone documentation. You can get it from http://www.graphviz.org/.")
else()
message(WARNING "The dot program is needed to generate the linphone documentation. You can get it from http://www.graphviz.org/.")
endif()
endif()
endif()
endif()

View file

@ -1186,6 +1186,7 @@ static void sound_config_read(LinphoneCore *lc)
#endif
tmp=lp_config_get_int(lc->config,"sound","echocancellation",tmp);
linphone_core_enable_echo_cancellation(lc,tmp);
linphone_core_set_echo_canceller_filter_name(lc, linphone_core_get_echo_canceller_filter_name(lc));
linphone_core_enable_echo_limiter(lc,
lp_config_get_int(lc->config,"sound","echolimiter",0));
linphone_core_enable_agc(lc,
@ -1204,7 +1205,7 @@ static void sound_config_read(LinphoneCore *lc)
static void certificates_config_read(LinphoneCore *lc) {
LinphoneFactory *factory = linphone_factory_get();
char *data_dir = linphone_factory_get_data_resources_dir(factory);
const char *data_dir = linphone_factory_get_data_resources_dir(factory);
char *root_ca_path = bctbx_strdup_printf("%s/rootca.pem", data_dir);
const char *rootca = lp_config_get_string(lc->config,"sip","root_ca", NULL);
// If rootca is not existing anymore, we reset it to the default value
@ -1225,7 +1226,6 @@ static void certificates_config_read(LinphoneCore *lc) {
linphone_core_verify_server_certificates(lc,lp_config_get_int(lc->config,"sip","verify_server_certs",TRUE));
linphone_core_verify_server_cn(lc,lp_config_get_int(lc->config,"sip","verify_server_cn",TRUE));
bctbx_free(root_ca_path);
bctbx_free(data_dir);
}
static void sip_config_read(LinphoneCore *lc) {
@ -2076,8 +2076,8 @@ static void linphone_core_init(LinphoneCore * lc, LinphoneCoreCbs *cbs, LpConfig
const char *remote_provisioning_uri = NULL;
LinphoneFactory *lfactory = linphone_factory_get();
LinphoneCoreCbs *internal_cbs = _linphone_core_cbs_new();
char *msplugins_dir;
char *image_resources_dir;
const char *msplugins_dir;
const char *image_resources_dir;
ms_message("Initializing LinphoneCore %s", linphone_core_get_version());
@ -2109,8 +2109,6 @@ static void linphone_core_init(LinphoneCore * lc, LinphoneCoreCbs *cbs, LpConfig
msplugins_dir = linphone_factory_get_msplugins_dir(lfactory);
image_resources_dir = linphone_factory_get_image_resources_dir(lfactory);
lc->factory = ms_factory_new_with_voip_and_directories(msplugins_dir, image_resources_dir);
if (msplugins_dir) bctbx_free(msplugins_dir);
bctbx_free(image_resources_dir);
linphone_core_register_default_codecs(lc);
linphone_core_register_offer_answer_providers(lc);
/* Get the mediastreamer2 event queue */

View file

@ -481,9 +481,11 @@ int linphone_config_read_file(LpConfig *lpconfig, const char *filename){
}
void lp_item_set_value(LpItem *item, const char *value){
char *prev_value=item->value;
item->value=ortp_strdup(value);
ortp_free(prev_value);
if (item->value != value) {
char *prev_value=item->value;
item->value=ortp_strdup(value);
ortp_free(prev_value);
}
}

View file

@ -1270,6 +1270,17 @@ const char *linphone_core_get_video_display_filter(LinphoneCore *lc){
return lp_config_get_string(lc->config,"video","displaytype",NULL);
}
void linphone_core_set_echo_canceller_filter_name(LinphoneCore *lc, const char *filtername) {
lp_config_set_string(lc->config, "sound", "ec_filter", filtername);
if (filtername != NULL) {
ms_factory_set_echo_canceller_filter_name(lc->factory, filtername);
}
}
const char * linphone_core_get_echo_canceller_filter_name(const LinphoneCore *lc) {
return lp_config_get_string(lc->config, "sound", "ec_filter", NULL);
}
/**
* Queue a task into the main loop. The data pointer must remain valid until the task is completed.
* task_fun must return BELLE_SIP_STOP when job is finished.

View file

@ -124,8 +124,11 @@
#ifdef __cplusplus
extern "C" {
#endif
#define ms_strdup_safe(str) ((str) ? ms_strdup(str) : NULL)
#define STRING_RESET(field) if (field) bctbx_free(field); (field) = NULL
#define STRING_SET(field, value) do{ if (field){bctbx_free(field);field=NULL;}; field=bctbx_strdup(value); }while(0)
#define STRING_TRANSFER(field, newvalue) do{ if (field){bctbx_free(field);field=NULL;}; field=newvalue; }while(0)
struct _LinphoneCallParams{
belle_sip_object_t base;

View file

@ -341,13 +341,12 @@ static int get_ui_file(const char *name, char *path, int pathsize){
snprintf(path,pathsize,"%s/%s.ui",BUILD_TREE_XML_DIR,name);
if (bctbx_file_exist(path)!=0){
LinphoneFactory *factory = linphone_factory_get();
char *data_dir = linphone_factory_get_data_resources_dir(factory);
const char *data_dir = linphone_factory_get_data_resources_dir(factory);
snprintf(path,pathsize,"%s/%s.ui",data_dir,name);
if (bctbx_file_exist(path)!=0){
g_error("Could not locate neither %s/%s.ui nor %s/%s.ui",BUILD_TREE_XML_DIR,name,data_dir,name);
return -1;
}
bctbx_free(data_dir);
}
return 0;
}
@ -486,7 +485,7 @@ static void about_url_clicked(GtkAboutDialog *dialog, const char *url, gpointer
void linphone_gtk_show_about(void){
struct stat filestat;
char *data_dir;
const char *data_dir;
char *license_file;
GtkWidget *about;
const char *tmp;
@ -501,7 +500,6 @@ void linphone_gtk_show_about(void){
data_dir = linphone_factory_get_data_resources_dir(factory);
license_file = bctbx_strdup_printf("%s/COPYING", data_dir);
bctbx_free(data_dir);
memset(&filestat,0,sizeof(filestat));
if (stat(license_file,&filestat)!=0){
license_file="COPYING";
@ -2136,7 +2134,7 @@ static void populate_xdg_data_dirs_envvar(void) {
int i;
gchar *value;
gchar **paths;
char *data_dir;
const char *data_dir;
LinphoneFactory *factory = linphone_factory_get();
if(g_getenv("XDG_DATA_DIRS") == NULL) {
@ -2152,7 +2150,6 @@ static void populate_xdg_data_dirs_envvar(void) {
g_setenv("XDG_DATA_DIRS", new_value, TRUE);
g_free(new_value);
}
bctbx_free(data_dir);
g_strfreev(paths);
#endif
}
@ -2168,7 +2165,7 @@ int main(int argc, char *argv[]){
char *chat_messages_db_file, *call_logs_db_file, *friends_db_file;
GError *error=NULL;
const char *tmp;
char *resources_dir;
const char *resources_dir;
char *pixmaps_dir;
LinphoneFactory *factory;
@ -2271,7 +2268,6 @@ int main(int argc, char *argv[]){
pixmaps_dir = bctbx_strdup_printf("%s/pixmaps/linphone", resources_dir);
add_pixmap_directory(pixmaps_dir);
bctbx_free(pixmaps_dir);
bctbx_free(resources_dir);
/* Now, look for the factory configuration file, we do it this late
since we want to have had time to change directory and to parse

View file

@ -193,7 +193,7 @@ void linphone_gtk_set_ui_config(const char *key , const char * val){
const char *linphone_gtk_get_sound_path(const char *name){
static char *ret=NULL;
const char *file;
char *sound_dir;
const char *sound_dir;
LinphoneFactory *factory = linphone_factory_get();
file=linphone_gtk_get_ui_config(name,NULL);
@ -212,7 +212,6 @@ const char *linphone_gtk_get_sound_path(const char *name){
}
sound_dir = linphone_factory_get_sound_resources_dir(factory);
ret=g_build_filename(sound_dir,name,NULL);
bctbx_free(sound_dir);
return ret;
}

View file

@ -4145,6 +4145,23 @@ LINPHONE_PUBLIC const char *linphone_core_get_video_display_filter(LinphoneCore
**/
LINPHONE_PUBLIC void linphone_core_set_video_display_filter(LinphoneCore *lc, const char *filtername);
/**
* Get the name of the mediastreamer2 filter used for echo cancelling.
* @param[in] lc LinphoneCore object
* @return The name of the mediastreamer2 filter used for echo cancelling
* @ingroup media_parameters
*/
LINPHONE_PUBLIC const char * linphone_core_get_echo_canceller_filter_name(const LinphoneCore *lc);
/**
* Set the name of the mediastreamer2 filter to be used for echo cancelling.
* This is for advanced users of the library.
* @param[in] lc LinphoneCore object
* @param[in] filtername The name of the mediastreamer2 filter to be used for echo cancelling
* @ingroup media_parameters
*/
LINPHONE_PUBLIC void linphone_core_set_echo_canceller_filter_name(LinphoneCore *lc, const char *filtername);
/** Contact Providers
*/

View file

@ -123,7 +123,7 @@ LINPHONE_PUBLIC LinphoneVcard *linphone_factory_create_vcard(LinphoneFactory *fa
* @param[in] factory LinphoneFactory object
* @return The path to the top directory where the resources are located
*/
LINPHONE_PUBLIC char * linphone_factory_get_top_resources_dir(const LinphoneFactory *factory);
LINPHONE_PUBLIC const char * linphone_factory_get_top_resources_dir(const LinphoneFactory *factory);
/**
* Set the top directory where the resources are located.
@ -138,7 +138,7 @@ LINPHONE_PUBLIC void linphone_factory_set_top_resources_dir(LinphoneFactory *fac
* @param[in] factory LinphoneFactory object
* @return The path to the directory where the data resources are located
*/
LINPHONE_PUBLIC char * linphone_factory_get_data_resources_dir(const LinphoneFactory *factory);
LINPHONE_PUBLIC const char * linphone_factory_get_data_resources_dir(LinphoneFactory *factory);
/**
* Set the directory where the data resources are located.
@ -152,7 +152,7 @@ LINPHONE_PUBLIC void linphone_factory_set_data_resources_dir(LinphoneFactory *fa
* @param[in] factory LinphoneFactory object
* @return The path to the directory where the sound resources are located
*/
LINPHONE_PUBLIC char * linphone_factory_get_sound_resources_dir(const LinphoneFactory *factory);
LINPHONE_PUBLIC const char * linphone_factory_get_sound_resources_dir(LinphoneFactory *factory);
/**
* Set the directory where the sound resources are located.
@ -166,7 +166,7 @@ LINPHONE_PUBLIC void linphone_factory_set_sound_resources_dir(LinphoneFactory *f
* @param[in] factory LinphoneFactory object
* @return The path to the directory where the ring resources are located
*/
LINPHONE_PUBLIC char * linphone_factory_get_ring_resources_dir(const LinphoneFactory *factory);
LINPHONE_PUBLIC const char * linphone_factory_get_ring_resources_dir(LinphoneFactory *factory);
/**
* Set the directory where the ring resources are located.
@ -180,7 +180,7 @@ LINPHONE_PUBLIC void linphone_factory_set_ring_resources_dir(LinphoneFactory *fa
* @param[in] factory LinphoneFactory object
* @return The path to the directory where the image resources are located
*/
LINPHONE_PUBLIC char * linphone_factory_get_image_resources_dir(const LinphoneFactory *factory);
LINPHONE_PUBLIC const char * linphone_factory_get_image_resources_dir(LinphoneFactory *factory);
/**
* Set the directory where the image resources are located.
@ -194,7 +194,7 @@ LINPHONE_PUBLIC void linphone_factory_set_image_resources_dir(LinphoneFactory *f
* @param[in] factory LinphoneFactory object
* @return The path to the directory where the mediastreamer2 plugins are located, or NULL if it has not been set
*/
LINPHONE_PUBLIC char * linphone_factory_get_msplugins_dir(const LinphoneFactory *factory);
LINPHONE_PUBLIC const char * linphone_factory_get_msplugins_dir(LinphoneFactory *factory);
/**
* Set the directory where the mediastreamer2 plugins are located.

@ -1 +1 @@
Subproject commit 1f17b3ba22cb662c1c15655c8248036e7a69baf2
Subproject commit b937df188829f73c3f5db35ff0191aa695f27884

View file

@ -17,6 +17,7 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE
#import <Foundation/Foundation.h>

View file

@ -141,11 +141,10 @@ int main(int argc, char *argv[]){
linphone_core_enable_echo_cancellation(lc, FALSE); /*no need for local echo cancellation when playing files*/
if (!media_file){
LinphoneFactory *factory = linphone_factory_get();
char *sound_resources_dir = linphone_factory_get_sound_resources_dir(factory);
const char *sound_resources_dir = linphone_factory_get_sound_resources_dir(factory);
char *play_file = bctbx_strdup_printf("%s/hello16000.wav", sound_resources_dir);
linphone_core_set_play_file(lc, play_file);
bctbx_free(play_file);
bctbx_free(sound_resources_dir);
linphone_core_set_preferred_framerate(lc,5);
}else{
PayloadType *pt = linphone_core_find_payload_type(lc, "opus", 48000, -1);

View file

@ -38,6 +38,7 @@ target_link_libraries(linphone++
target_include_directories(linphone++
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include
PRIVATE ${CMAKE_BINARY_DIR}/include
PRIVATE ${CMAKE_SOURCE_DIR}/include
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${BCTOOLBOX_INCLUDE_DIRS}
PRIVATE ${BELLESIP_INCLUDE_DIRS}

View file

@ -29,16 +29,21 @@
# LINPHONECXX_INCLUDE_DIRS - the linphone++ include directory
# LINPHONECXX_LIBRARIES - The libraries needed to use linphone++
# LINPHONECXX_LDFLAGS - The linking flags needed to use linphone++
include("${CMAKE_CURRENT_LIST_DIR}/LinphoneCxxTargets.cmake")
if (NOT LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
include("${CMAKE_CURRENT_LIST_DIR}/LinphoneCxxTargets.cmake")
endif()
set(LINPHONECXX_LDFLAGS "")
set(LINPHONECXX_CPPFLAGS "")
set(LINPHONECXX_LIBRARIES linphone++)
#get_target_property(LINPHONECXX_INCLUDE_DIRS linphone++ INTERFACE_INCLUDE_DIRECTORIES)
#list(INSERT LINPHONECXX_INCLUDE_DIRS 0 "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
set(LINPHONECXX_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
list(APPEND LINPHONECXX_INCLUDE_DIRS "@CMAKE_CURRENT_BINARY_DIR@/include")
list(APPEND LINPHONECXX_INCLUDE_DIRS "${EP_linphone_INCLUDE_DIR}/../wrappers/cpp")
else()
set(LINPHONECXX_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
endif()
list(REMOVE_DUPLICATES LINPHONECXX_INCLUDE_DIRS)
set(LINPHONECXX_FOUND 1)

View file

@ -178,7 +178,7 @@ class ArgName(Name):
return Name.to_snake_case(self)
class PropertyName(Name):
class PropertyName(ArgName):
pass
@ -357,6 +357,7 @@ class Property(DocumentableObject):
DocumentableObject.__init__(self, name)
self._setter = None
self._getter = None
self._type = None
def set_setter(self, setter):
self._setter = setter
@ -367,6 +368,8 @@ class Property(DocumentableObject):
def set_getter(self, getter):
self._getter = getter
if self._type is None:
self._type = getter.returnType
getter.parent = self
def get_getter(self):
@ -384,6 +387,7 @@ class Class(DocumentableObject):
self.classMethods = []
self._listenerInterface = None
self.multilistener = False
self.refcountable = False
def add_property(self, property):
self.properties.append(property)
@ -482,13 +486,21 @@ class CParser(object):
'linphone_friend_new', # was deprecated when the wrapper generator was made
'linphone_friend_new_with_address', # was deprecated when the wrapper generator was made
'linphone_core_get_sound_source', # was deprecated when the wrapper generator was made
'linphone_core_set_sound_source'] # was deprecated when the wrapper generator was made
'linphone_core_set_sound_source', # was deprecated when the wrapper generator was made
'linphone_core_get_sip_transports', # not wrappable
'linphone_core_get_sip_transports_used'] # not wrappable
self.classBl = ['LinphoneImEncryptionEngine',
'LinphoneImEncryptionEngineCbs',
'LinphoneImNotifPolicy',
'LpConfig',
'LinphoneCallStats'] # temporarly blacklisted
'LinphoneErrorInfo',
'LinphonePayloadType',
'LinphonePlayer'] # temporarly blacklisted
# list of classes that must be concidered as refcountable even if
# they are no ref()/unref() methods
self.forcedRefcountableClasses = ['LinphoneFactory']
self.cProject = cProject
@ -531,8 +543,19 @@ class CParser(object):
pass
except Error as e:
print('Could not parse \'{0}\' class: {1}'.format(_class.name, e.args[0]))
CParser._fix_all_types(self)
def _class_is_refcountable(self, _class):
if _class.name in self.forcedRefcountableClasses:
return True
for method in _class.instanceMethods:
if method.startswith(_class.cFunctionPrefix) and method[len(_class.cFunctionPrefix):] == 'ref':
return True
return False
def _fix_all_types(self):
for _class in self.classesIndex.values() + self.interfacesIndex.values():
if _class is not None:
@ -563,24 +586,24 @@ class CParser(object):
except Error as e:
print('warning: some types could not be fixed in {0}() function: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0]))
def _fix_type(self, type):
if isinstance(type, EnumType) and type.desc is None:
type.desc = self.enumsIndex[type.name]
elif isinstance(type, ClassType) and type.desc is None:
if type.name in self.classesIndex:
type.desc = self.classesIndex[type.name]
def _fix_type(self, _type):
if isinstance(_type, EnumType) and _type.desc is None:
_type.desc = self.enumsIndex[_type.name]
elif isinstance(_type, ClassType) and _type.desc is None:
if _type.name in self.classesIndex:
_type.desc = self.classesIndex[_type.name]
else:
type.desc = self.interfacesIndex[type.name]
elif isinstance(type, ListType) and type.containedTypeDesc is None:
if type.containedTypeName in self.classesIndex:
type.containedTypeDesc = ClassType(type.containedTypeName, classDesc=self.classesIndex[type.containedTypeName])
elif type.containedTypeName in self.interfacesIndex:
type.containedTypeDesc = ClassType(type.containedTypeName, classDesc=self.interfacesIndex[type.containedTypeName])
elif type.containedTypeName in self.enumsIndex:
type.containedTypeDesc = EnumType(type.containedTypeName, enumDesc=self.enumsIndex[type.containedTypeName])
_type.desc = self.interfacesIndex[_type.name]
elif isinstance(_type, ListType) and _type.containedTypeDesc is None:
if _type.containedTypeName in self.classesIndex:
_type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.classesIndex[_type.containedTypeName])
elif _type.containedTypeName in self.interfacesIndex:
_type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.interfacesIndex[_type.containedTypeName])
elif _type.containedTypeName in self.enumsIndex:
_type.containedTypeDesc = EnumType(_type.containedTypeName, enumDesc=self.enumsIndex[_type.containedTypeName])
else:
if type.containedTypeName is not None:
type.containedTypeDesc = CParser.parse_c_base_type(self, type.containedTypeName)
if _type.containedTypeName is not None:
_type.containedTypeDesc = CParser.parse_c_base_type(self, _type.containedTypeName)
else:
raise Error('bctbx_list_t type without specified contained type')
@ -621,6 +644,7 @@ class CParser(object):
name = ClassName()
name.from_camel_case(cclass.name, namespace=self.namespace.name)
_class = Class(name)
_class.refcountable = CParser._class_is_refcountable(self, cclass)
for cproperty in cclass.properties.values():
try:
@ -755,10 +779,8 @@ class CParser(object):
elif cType.ctype in self.enumsIndex:
absType = EnumType(cType.ctype, enumDesc=self.enumsIndex[cType.ctype])
elif cType.ctype in self.classesIndex or cType.ctype in self.interfacesIndex:
#params = {'classDesc': self.classesIndex[cType.ctype]}
#if 'const' in cType.completeType.split(' '):
#params['isconst'] = True
absType = ClassType(cType.ctype)
absType.isconst = cType.completeType.startswith('const ')
elif cType.ctype == self.cListType:
absType = ListType(cType.containedType)
else:

View file

@ -43,14 +43,23 @@ namespace linphone {
{{/priorDeclarations}}
{{#_class}}
class {{className}}: public {{{parentClassName}}} {
class {{className}}{{#parentClassName}}: public {{{parentClassName}}}{{/parentClassName}} {
{{#friendClasses}}
friend class {{name}};
{{/friendClasses}}
public:
{{#isNotListener}}
{{#isrefcountable}}
{{{className}}}(void *ptr, bool takeRef=true);
{{/isrefcountable}}
{{#isnotrefcountable}}
LINPHONECXX_PUBLIC {{{className}}}();
LINPHONECXX_PUBLIC {{{className}}}(const {{{className}}} &src);
{{{className}}}(const void *src);
LINPHONECXX_PUBLIC ~{{{className}}}();
LINPHONECXX_PUBLIC const void *c_struct() const {return mPrivPtr;}
{{/isnotrefcountable}}
{{/isNotListener}}
{{#ismonolistenable}}
@ -71,6 +80,7 @@ namespace linphone {
{{#isVcard}}
LINPHONECXX_PUBLIC std::shared_ptr<belcard::BelCard> &getVcard();
{{/isVcard}}
{{#methods}}
{{{prototype}}}
@ -91,7 +101,11 @@ namespace linphone {
private:
void *mCallbacks;
{{/ismultilistenable}}
{{#isnotrefcountable}}
private:
void *mPrivPtr;
{{/isnotrefcountable}}
};
{{/_class}}

View file

@ -47,12 +47,33 @@ static {{{returnType}}} {{{cbName}}}({{{declArgs}}}) {
{{/wrapperCbs}}
{{#isNotListener}}
{{#isrefcountable}}
{{{namespace}}}::{{{className}}}::{{{className}}}(void *ptr, bool takeRef): {{{parentClassName}}}(ptr, takeRef) {
{{#ismultilistenable}}
mCallbacks = ({{{cListenerName}}} *)createCallbacks(&getListeners());
{{{callbacksAdder}}}(({{{cClassName}}} *)mPrivPtr, ({{{cListenerName}}} *)mCallbacks);
{{/ismultilistenable}}
}
{{/isrefcountable}}
{{#isnotrefcountable}}
{{{namespace}}}::{{{className}}}::{{{className}}}() {
mPrivPtr = new {{{cClassName}}};
memset(mPrivPtr, 0, sizeof({{{cClassName}}}));
}
{{{namespace}}}::{{{className}}}::{{{className}}}(const {{{className}}} &src) {
mPrivPtr = new {{{cClassName}}}(*({{{cClassName}}} *)src.mPrivPtr);
}
{{{namespace}}}::{{{className}}}::{{{className}}}(const void *src) {
mPrivPtr = new {{{cClassName}}}(*({{{cClassName}}} *)src);
}
{{{namespace}}}::{{{className}}}::~{{{className}}}() {
delete ({{{cClassName}}} *)mPrivPtr;
}
{{/isnotrefcountable}}
{{/isNotListener}}
{{#ismonolistenable}}

View file

@ -66,22 +66,24 @@ class CppTranslator(object):
ismonolistenable = (islistenable and not _class.multilistener)
ismultilistenable = (islistenable and _class.multilistener)
classDict = {}
classDict['islistenable'] = islistenable
classDict['isnotlistenable'] = not islistenable
classDict['ismonolistenable'] = ismonolistenable
classDict['ismultilistenable'] = ismultilistenable
classDict['isNotListener'] = True
classDict['isfactory'] = (_class.name.to_c() == 'LinphoneFactory')
classDict['isVcard'] = (_class.name.to_c() == 'LinphoneVcard')
classDict['parentClassName'] = None
classDict['className'] = CppTranslator.translate_class_name(_class.name)
classDict['cClassName'] = '::' + _class.name.to_c()
classDict['parentClassName'] = 'Object'
classDict['methods'] = []
classDict['staticMethods'] = []
classDict['wrapperCbs'] = []
classDict['friendClasses'] = []
classDict = {
'islistenable' : islistenable,
'isnotlistenable' : not islistenable,
'ismonolistenable' : ismonolistenable,
'ismultilistenable' : ismultilistenable,
'isrefcountable' : _class.refcountable,
'isnotrefcountable' : not _class.refcountable,
'isNotListener' : True,
'isfactory' : (_class.name.to_c() == 'LinphoneFactory'),
'isVcard' : (_class.name.to_c() == 'LinphoneVcard'),
'className' : CppTranslator.translate_class_name(_class.name),
'cClassName' : '::' + _class.name.to_c(),
'parentClassName' : 'Object' if _class.refcountable else None,
'methods' : [],
'staticMethods' : [],
'wrapperCbs' : [],
'friendClasses' : []
}
if _class.name.to_c() == 'LinphoneCore':
classDict['friendClasses'].append({'name': 'Factory'});
@ -279,20 +281,33 @@ class CppTranslator(object):
elif type(exprtype) is AbsApi.EnumType:
cExpr = '(::{0}){1}'.format(exprtype.desc.name.to_c(), cppExpr)
elif type(exprtype) is AbsApi.ClassType:
param = {}
param['ptrType'] = CppTranslator.translate_class_type(self, exprtype, namespace=usedNamespace)
param['ptrType'] = CppTranslator.sharedPtrTypeExtractor.match(param['ptrType']).group(2)
param['cPtrType'] = exprtype.desc.name.to_c()
param['cppExpr'] = cppExpr
param['object'] = 'const Object' if exprtype.isconst else 'Object'
cExpr = '(::{cPtrType} *)Object::sharedPtrToCPtr(std::static_pointer_cast<{object},{ptrType}>({cppExpr}))'.format(**param)
cPtrType = exprtype.desc.name.to_c()
if exprtype.desc.refcountable:
ptrType = CppTranslator.translate_class_type(self, exprtype, namespace=usedNamespace)
ptrType = CppTranslator.sharedPtrTypeExtractor.match(ptrType).group(2)
param = {
'ptrType' : ptrType,
'cPtrType': cPtrType,
'cppExpr' : cppExpr,
'object' : 'const Object' if exprtype.isconst else 'Object'
}
cExpr = '(::{cPtrType} *)Object::sharedPtrToCPtr(std::static_pointer_cast<{object},{ptrType}>({cppExpr}))'.format(**param)
else:
cExpr = '(const ::{_type} *)({expr}).c_struct()'.format(_type=cPtrType, expr=cppExpr)
elif type(exprtype) is AbsApi.ListType:
if type(exprtype.containedTypeDesc) is AbsApi.BaseType and exprtype.containedTypeDesc.name == 'string':
cExpr = 'StringBctbxListWrapper({0}).c_list()'.format(cppExpr)
elif type(exprtype.containedTypeDesc) is AbsApi.ClassType:
ptrType = CppTranslator.translate_class_type(self, exprtype.containedTypeDesc, namespace=usedNamespace)
ptrType = CppTranslator.sharedPtrTypeExtractor.match(ptrType).group(2)
cExpr = 'ObjectBctbxListWrapper<{0}>({1}).c_list()'.format(ptrType, cppExpr)
if exprtype.containedTypeDesc.desc.refcountable:
ptrType = CppTranslator.sharedPtrTypeExtractor.match(ptrType).group(2)
cExpr = 'ObjectBctbxListWrapper<{0}>({1}).c_list()'.format(ptrType, cppExpr)
else:
cType = exprtype.containedTypeDesc.desc.name.to_c()
if exprtype.isconst:
cExpr = 'StructBctbxListWrapper<{0},{1}>({2}).c_list()'.format(ptrType, cType, cppExpr)
else:
cExpr = 'StructBctbxListWrapper<{0},{1}>::cppListToBctbxList({2})'.format(ptrType, cType, cppExpr)
else:
raise AbsApi.Error('translation of bctbx_list_t of enums or basic C types is not supported')
@ -315,19 +330,26 @@ class CppTranslator(object):
return '({0}){1}'.format(cppEnumName, cExpr)
elif type(exprtype) is AbsApi.ClassType:
cppReturnType = CppTranslator.translate_class_type(self, exprtype, namespace=usedNamespace)
cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2)
if type(exprtype.parent) is AbsApi.Method and len(exprtype.parent.name.words) >=1 and (exprtype.parent.name.words == ['new'] or exprtype.parent.name.words[0] == 'create'):
return 'Object::cPtrToSharedPtr<{0}>((::belle_sip_object_t *){1}, false)'.format(cppReturnType, cExpr)
if exprtype.desc.refcountable:
cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2)
if type(exprtype.parent) is AbsApi.Method and len(exprtype.parent.name.words) >=1 and (exprtype.parent.name.words == ['new'] or exprtype.parent.name.words[0] == 'create'):
return 'Object::cPtrToSharedPtr<{0}>({1}, false)'.format(cppReturnType, cExpr)
else:
return 'Object::cPtrToSharedPtr<{0}>({1})'.format(cppReturnType, cExpr)
else:
return 'Object::cPtrToSharedPtr<{0}>((::belle_sip_object_t *){1})'.format(cppReturnType, cExpr)
return '{0}({1})'.format(exprtype.desc.name.to_camel_case(), cExpr);
elif type(exprtype) is AbsApi.ListType:
if type(exprtype.containedTypeDesc) is AbsApi.BaseType and exprtype.containedTypeDesc.name == 'string':
return 'StringBctbxListWrapper::bctbxListToCppList({0})'.format(cExpr)
elif type(exprtype.containedTypeDesc) is AbsApi.ClassType:
cppReturnType = CppTranslator.translate_class_type(self, exprtype.containedTypeDesc, namespace=usedNamespace)
cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2)
return 'ObjectBctbxListWrapper<{0}>::bctbxListToCppList({1})'.format(cppReturnType, cExpr)
if exprtype.containedTypeDesc.desc.refcountable:
cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2)
return 'ObjectBctbxListWrapper<{0}>::bctbxListToCppList({1})'.format(cppReturnType, cExpr)
else:
cType = exprtype.containedTypeDesc.desc.name.to_c()
return 'StructBctbxListWrapper<{0},{1}>::bctbxListToCppList({2})'.format(cppReturnType, cType, cExpr)
else:
raise AbsApi.Error('translation of bctbx_list_t of enums or basic C types is not supported')
else:
@ -429,13 +451,18 @@ class CppTranslator(object):
res = CppTranslator.translate_class_name(_type.desc.name, recursive=True, topAncestor=nsName)
if _type.isconst:
res = 'const ' + res
if type(_type.parent) is AbsApi.Argument:
return 'const std::shared_ptr<{0}> &'.format(res)
if _type.desc.refcountable:
if _type.isconst:
res = 'const ' + res
if type(_type.parent) is AbsApi.Argument:
return 'const std::shared_ptr<{0}> &'.format(res)
else:
return 'std::shared_ptr<{0}>'.format(res)
else:
return 'std::shared_ptr<{0}>'.format(res)
if type(_type.parent) is AbsApi.Argument:
return 'const {0} &'.format(res)
else:
return '{0}'.format(res)
def translate_list_type(self, _type, **params):
if _type.containedTypeDesc is None:
@ -679,6 +706,8 @@ def main():
render(renderer, header, includedir + '/enums.hh')
mainHeader = MainHeader()
mainHeader.add_include('enums.hh')
impl = ClassImpl()
for _class in parser.classesIndex.values() + parser.interfacesIndex.values():

View file

@ -72,7 +72,7 @@ std::map<std::string,void *> &Object::getUserData() const {
return *userData;
}
Object *Object::getBackPtrFromCPtr(void *ptr) {
linphone::Object *linphone::Object::getBackPtrFromCPtr(const void *ptr) {
return (Object *)belle_sip_object_data_get((::belle_sip_object_t *)ptr, "cpp_object");
}

View file

@ -85,11 +85,25 @@ namespace linphone {
}
}
}
template <class T>
static std::shared_ptr<const T> cPtrToSharedPtr(const void *ptr, bool takeRef=true) {
if (ptr == NULL) {
return nullptr;
} else {
Object *cppPtr = getBackPtrFromCPtr(ptr);
if (cppPtr == NULL) {
return std::make_shared<const T>((void *)ptr, takeRef);
} else {
return std::static_pointer_cast<const T,Object>(cppPtr->shared_from_this());
}
}
}
static void *sharedPtrToCPtr(const std::shared_ptr<const Object> &sharedPtr);
private:
LINPHONECXX_PUBLIC std::map<std::string,void *> &getUserData() const;
static Object *getBackPtrFromCPtr(void *ptr);
static Object *getBackPtrFromCPtr(const void *ptr);
template <class T> static void deleteSharedPtr(std::shared_ptr<T> *ptr) {if (ptr != NULL) delete ptr;}
static void deleteString(std::string *str) {if (str != NULL) delete str;}

View file

@ -43,14 +43,12 @@ namespace linphone {
class ObjectBctbxListWrapper: public AbstractBctbxListWrapper {
public:
ObjectBctbxListWrapper(const std::list<std::shared_ptr<T> > &cppList) {
for(auto it=cppList.cbegin(); it!=cppList.cend(); it++) {
::belle_sip_object_t *cPtr = (::belle_sip_object_t *)Object::sharedPtrToCPtr(std::static_pointer_cast<Object,T>(*it));
if (cPtr != NULL) belle_sip_object_ref(cPtr);
mCList = bctbx_list_append(mCList, cPtr);
}
mCList = cppListToBctbxList(cppList);
}
virtual ~ObjectBctbxListWrapper() {
mCList = bctbx_list_free_with_data(mCList, unrefData);
if (mCList != NULL) {
bctbx_list_free_with_data(mCList, unrefData);
}
}
static std::list<std::shared_ptr<T> > bctbxListToCppList(const ::bctbx_list_t *bctbxList) {
std::list<std::shared_ptr<T> > cppList;
@ -60,6 +58,15 @@ namespace linphone {
}
return cppList;
}
static ::bctbx_list_t *cppListToBctbxList(const std::list<std::shared_ptr<T> > &cppList) {
bctbx_list_t *cList = NULL;
for(auto it=cppList.cbegin(); it!=cppList.cend(); it++) {
::belle_sip_object_t *cPtr = (::belle_sip_object_t *)Object::sharedPtrToCPtr(std::static_pointer_cast<Object,T>(*it));
if (cPtr != NULL) belle_sip_object_ref(cPtr);
cList = bctbx_list_append(cList, cPtr);
}
return cList;
}
private:
static void unrefData(void *data) {
@ -75,6 +82,35 @@ namespace linphone {
static std::list<std::string> bctbxListToCppList(const ::bctbx_list_t *bctbxList);
};
template <class T, class U>
class StructBctbxListWrapper: public AbstractBctbxListWrapper {
public:
StructBctbxListWrapper(const std::list<T> &cppList): AbstractBctbxListWrapper() {
mCList = cppListToBctbxList(cppList);
}
virtual ~StructBctbxListWrapper() {
bctbx_list_free_with_data(mCList, (bctbx_list_free_func)deleteCStruct);
}
static std::list<T> bctbxListToCppList(const ::bctbx_list_t *bctbxList) {
std::list<T> cppList;
for(const bctbx_list_t *it = bctbx_list_first_elem(bctbxList); it != NULL; it = bctbx_list_next(it)) {
cppList->push_back(T(it->data));
}
return cppList;
}
static bctbx_list_t *cppListToBctbxList(const std::list<T> &cppList) {
bctbx_list_t *cList = NULL;
for(auto it=cppList.cbegin(); it!=cppList.cend(); it++) {
cList = bctbx_list_append(cList, new U(it->c_struct()));
}
return cList;
}
private:
static void deleteCStruct(U *cStruct) {delete cStruct;}
};
class StringUtilities {
public:
static std::string cStringToCpp(const char *cstr);