diff --git a/source/tests/Makefile b/source/tests/Makefile index a8e5af8ab..97a0f1e8c 100644 --- a/source/tests/Makefile +++ b/source/tests/Makefile @@ -11,6 +11,7 @@ include ../Makefile.mk BUILD_CXX_FLAGS += -I../backend -I../includes -I../modules -I../modules/distrho -I../utils BUILD_CXX_FLAGS += -DWANT_NATIVE -DWANT_LADSPA -DWANT_DSSI -DWANT_LV2 -DWANT_VST -DWANT_AU -DWANT_CSOUND -DWANT_FLUIDSYNTH -DWANT_LINUXSAMPLER BUILD_CXX_FLAGS += -DWANT_OPENGL -DWANT_AUDIOFILE -DWANT_MIDIFILE -DWANT_ZYNADDSUBFX -DWANT_ZYNADDSUBFX_UI +BUILD_CXX_FLAGS += -std=c++11 -Wzero-as-null-pointer-constant BUILD_CXX_FLAGS += -isystem /opt/kxstudio/include # BUILD_CXX_FLAGS += -isystem /usr/include/qt5 # BUILD_CXX_FLAGS += -I/opt/mingw32/include @@ -18,7 +19,7 @@ BUILD_CXX_FLAGS += -isystem /opt/kxstudio/include ANSI_CXX_FLAGS = -DBUILD_ANSI_TEST -DVESTIGE_HEADER ANSI_CXX_FLAGS += -ansi -pedantic -pedantic-errors -Wunused-parameter -Wuninitialized -Wno-vla ANSI_CXX_FLAGS += -Wcast-qual -Wconversion -Wsign-conversion -Wlogical-op -Waggregate-return -ANSI_CXX_FLAGS += -std=c++11 -Wzero-as-null-pointer-constant +# ANSI_CXX_FLAGS += -std=c++11 -Wzero-as-null-pointer-constant TARGETS = CarlaString Print RtList Utils @@ -47,7 +48,7 @@ RtList: RtList.cpp ../utils/RtList.hpp ../modules/rtmempool.a # valgrind ./RtList Utils: Utils.cpp - $(CXX) $^ $(BUILD_CXX_FLAGS) $(ANSI_CXX_FLAGS) $(LINK_FLAGS) -o $@ + $(CXX) $^ $(BUILD_CXX_FLAGS) $(ANSI_CXX_FLAGS) -std=c++11 -Wzero-as-null-pointer-constant $(LINK_FLAGS) -ldl -o $@ # valgrind ./Utils # -------------------------------------------------------------- diff --git a/source/tests/Print.cpp b/source/tests/Print.cpp index d5b6fd1df..a9f7d6e01 100644 --- a/source/tests/Print.cpp +++ b/source/tests/Print.cpp @@ -25,3 +25,5 @@ int main() carla_stderr2("STDERR2"); return 0; } + +#include "../utils/Utils.cpp" diff --git a/source/tests/Utils.cpp b/source/tests/Utils.cpp index c90a1f70a..55c7f7e7e 100644 --- a/source/tests/Utils.cpp +++ b/source/tests/Utils.cpp @@ -16,6 +16,10 @@ */ #include "CarlaUtils.hpp" +#include "CarlaLibUtils.hpp" +#include "CarlaJuceUtils.hpp" + +#include "CarlaString.hpp" #include #include @@ -27,6 +31,20 @@ struct MyStruct { void* ptr; }; +class MyLeakCheckedClass +{ +public: + MyLeakCheckedClass() {} + ~MyLeakCheckedClass() {} +private: + char pad[100]; + int i; + double d; + void* ptr; + + CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MyLeakCheckedClass) +}; + int main() { // misc functions @@ -34,7 +52,11 @@ int main() bool2str(true); pass(); - // string print functions, handled in Print + // string print functions + carla_debug("DEBUG"); + carla_stdout("STDOUT"); + carla_stderr("STDERR"); + carla_stderr2("STDERR2"); // carla_*sleep carla_sleep(1); @@ -156,6 +178,17 @@ int main() carla_copyStruct(d, c, 2); } + // Leak check + { + MyLeakCheckedClass a; + MyLeakCheckedClass* const b(new MyLeakCheckedClass); + delete b; + } + + // String + { + } + return 0; } diff --git a/source/utils/CarlaLibUtils.hpp b/source/utils/CarlaLibUtils.hpp index 6921176da..9bb4509ba 100644 --- a/source/utils/CarlaLibUtils.hpp +++ b/source/utils/CarlaLibUtils.hpp @@ -20,71 +20,31 @@ #include "CarlaUtils.hpp" -#ifndef CARLA_OS_WIN -# include -#endif - // ----------------------------------------------------------------------- // library related calls -static inline -void* lib_open(const char* const filename) -{ - CARLA_SAFE_ASSERT_RETURN(filename != nullptr, nullptr); - -#ifdef CARLA_OS_WIN - return (void*)LoadLibraryA(filename); -#else - return dlopen(filename, RTLD_NOW|RTLD_LOCAL); -#endif -} - -static inline -bool lib_close(void* const lib) -{ - CARLA_SAFE_ASSERT_RETURN(lib != nullptr, false); - -#ifdef CARLA_OS_WIN - return FreeLibrary((HMODULE)lib); -#else - return (dlclose(lib) == 0); -#endif -} - -static inline -void* lib_symbol(void* const lib, const char* const symbol) -{ - CARLA_SAFE_ASSERT_RETURN(lib != nullptr, nullptr); - CARLA_SAFE_ASSERT_RETURN(symbol != nullptr, nullptr); - -#ifdef CARLA_OS_WIN - return (void*)GetProcAddress((HMODULE)lib, symbol); -#else - return dlsym(lib, symbol); -#endif -} - -static inline -const char* lib_error(const char* const filename) -{ - CARLA_SAFE_ASSERT_RETURN(filename != nullptr, nullptr); - -#ifdef CARLA_OS_WIN - static char libError[2048+1]; - carla_zeroChar(libError, 2048+1); +/* + * Open 'filename' library (must not be NULL). + * May return NULL, in which case "lib_error" has the error. + */ +void* lib_open(const char* const filename); - LPVOID winErrorString; - DWORD winErrorCode = GetLastError(); - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, winErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&winErrorString, 0, nullptr); +/* + * Close a previously opened library (must not be NULL). + * If false is returned,"lib_error" has the error. + */ +bool lib_close(void* const lib); - std::snprintf(libError, 2048, "%s: error code %li: %s", filename, winErrorCode, (const char*)winErrorString); - LocalFree(winErrorString); +/* + * Get a library symbol (must not be NULL). + * May return NULL if the symbol is not found. + */ +void* lib_symbol(void* const lib, const char* const symbol); - return libError; -#else - return dlerror(); -#endif -} +/* + * Return the last operation error ('filename' must not be NULL). + */ +const char* lib_error(const char* const filename); // ----------------------------------------------------------------------- diff --git a/source/utils/CarlaString.hpp b/source/utils/CarlaString.hpp index 2dea444e5..c1c8ffed6 100644 --- a/source/utils/CarlaString.hpp +++ b/source/utils/CarlaString.hpp @@ -20,6 +20,8 @@ #include "CarlaJuceUtils.hpp" +#include // for *printf + // ----------------------------------------------------------------------- // CarlaString class @@ -29,24 +31,36 @@ public: // ------------------------------------------------------------------- // constructors (no explicit conversions allowed) + /* + * Empty string. + */ explicit CarlaString() { _init(); _dup(nullptr); } + /* + * Simple char string. + */ explicit CarlaString(char* const strBuf) { _init(); _dup(strBuf); } + /* + * Simple const char string. + */ explicit CarlaString(const char* const strBuf) { _init(); _dup(strBuf); } + /* + * Integer. + */ explicit CarlaString(const int value) { char strBuf[0xff+1]; @@ -57,6 +71,9 @@ public: _dup(strBuf); } + /* + * Unsigned integer, possibly in hexadecimal. + */ explicit CarlaString(const unsigned int value, const bool hexadecimal = false) { char strBuf[0xff+1]; @@ -67,6 +84,9 @@ public: _dup(strBuf); } + /* + * Long integer. + */ explicit CarlaString(const long int value) { char strBuf[0xff+1]; @@ -77,6 +97,9 @@ public: _dup(strBuf); } + /* + * Long unsigned integer, possibly hexadecimal. + */ explicit CarlaString(const unsigned long int value, const bool hexadecimal = false) { char strBuf[0xff+1]; @@ -87,6 +110,9 @@ public: _dup(strBuf); } + /* + * Single-precision floating point number. + */ explicit CarlaString(const float value) { char strBuf[0xff+1]; @@ -97,6 +123,9 @@ public: _dup(strBuf); } + /* + * Double-precision floating point number. + */ explicit CarlaString(const double value) { char strBuf[0xff+1]; @@ -110,6 +139,9 @@ public: // ------------------------------------------------------------------- // non-explicit constructor + /* + * Create string from another string. + */ CarlaString(const CarlaString& str) { _init(); @@ -119,6 +151,9 @@ public: // ------------------------------------------------------------------- // destructor + /* + * Destructor. + */ ~CarlaString() { CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,); @@ -130,21 +165,33 @@ public: // ------------------------------------------------------------------- // public methods + /* + * Get length of the string. + */ size_t length() const noexcept { return fBufferLen; } + /* + * Check if the string is empty. + */ bool isEmpty() const noexcept { return (fBufferLen == 0); } + /* + * Check if the string is not empty. + */ bool isNotEmpty() const noexcept { return (fBufferLen != 0); } + /* + * Check if the string contains another string, optionally ignoring case. + */ bool contains(const char* const strBuf, const bool ignoreCase = false) const { CARLA_SAFE_ASSERT_RETURN(strBuf != nullptr, false); @@ -164,11 +211,17 @@ public: return (std::strstr(fBuffer, strBuf) != nullptr); } + /* + * Overloaded function. + */ bool contains(const CarlaString& str, const bool ignoreCase = false) const { return contains(str.fBuffer, ignoreCase); } + /* + * Check if character at 'pos' is a digit. + */ bool isDigit(const size_t pos) const noexcept { CARLA_SAFE_ASSERT_RETURN(pos < fBufferLen, false); @@ -176,6 +229,9 @@ public: return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9'); } + /* + * Check if the string starts with the character 'c'. + */ bool startsWith(const char c) const { CARLA_SAFE_ASSERT_RETURN(c != '\0', false); @@ -183,6 +239,9 @@ public: return (fBufferLen > 0 && fBuffer[0] == c); } + /* + * Check if the string starts with the string 'prefix'. + */ bool startsWith(const char* const prefix) const { CARLA_SAFE_ASSERT_RETURN(prefix != nullptr, false); @@ -195,6 +254,9 @@ public: return (std::strncmp(fBuffer + (fBufferLen-prefixLen), prefix, prefixLen) == 0); } + /* + * Check if the string ends with the character 'c'. + */ bool endsWith(const char c) const { CARLA_SAFE_ASSERT_RETURN(c != '\0', false); @@ -202,6 +264,9 @@ public: return (fBufferLen > 0 && fBuffer[fBufferLen] == c); } + /* + * Check if the string ends with the string 'suffix'. + */ bool endsWith(const char* const suffix) const { CARLA_SAFE_ASSERT_RETURN(suffix != nullptr, false); @@ -214,36 +279,110 @@ public: return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0); } - void clear() noexcept + /* + * Find the first occurrence of character 'c' in the string. + * Returns "length()" if the character is not found. + */ + size_t find(const char c, bool* const found = nullptr) const noexcept { - truncate(0); - } + if (fBufferLen == 0 || c == '\0') + { + if (found != nullptr) + *found = false; + return fBufferLen; + } - size_t find(const char c) const noexcept - { for (size_t i=0; i < fBufferLen; ++i) { if (fBuffer[i] == c) + { + if (found != nullptr) + *found = true; return i; + } + } + + if (found != nullptr) + *found = false; + return fBufferLen; + } + + /* + * Find the first occurrence of string 'strBuf' in the string. + * Returns "length()" if the string is not found. + */ + size_t find(const char* const strBuf, bool* const found = nullptr) const + { + if (fBufferLen == 0 || strBuf != nullptr || strBuf[0] != '\0') + { + if (found != nullptr) + *found = false; + return fBufferLen; + } + + if (char* const subStrBuf = std::strstr(fBuffer, strBuf)) + { + const ssize_t ret(subStrBuf - fBuffer); + + if (ret < 0) + { + // should never happen! + carla_stderr2("Carla assertion failure: \"ret >= 0\" in file %s, line %i, value %i", __FILE__, __LINE__, ret); + + if (found != nullptr) + *found = false; + return fBufferLen; + } + + if (found != nullptr) + *found = true; + return static_cast(ret); } - return 0; + if (found != nullptr) + *found = false; + return fBufferLen; } - size_t rfind(const char c) const noexcept + /* + * Find the last occurrence of character 'c' in the string. + * Returns "length()" if the character is not found. + */ + size_t rfind(const char c, bool* const found = nullptr) const noexcept { + if (fBufferLen == 0 || c == '\0') + { + if (found != nullptr) + *found = false; + return fBufferLen; + } + for (size_t i=fBufferLen; i > 0; --i) { if (fBuffer[i-1] == c) + { + if (found != nullptr) + *found = true; return i-1; + } } - return 0; + if (found != nullptr) + *found = false; + return fBufferLen; } - size_t rfind(const char* const strBuf) const + /* + * Find the last occurrence of string 'strBuf' in the string. + * Returns "length()" if the string is not found. + */ + size_t rfind(const char* const strBuf, bool* const found = nullptr) const { - CARLA_SAFE_ASSERT_RETURN(strBuf != nullptr && strBuf[0] != '\0', fBufferLen); + if (found != nullptr) + *found = false; + + if (fBufferLen == 0 || strBuf != nullptr || strBuf[0] != '\0') + return fBufferLen; size_t ret = fBufferLen+1; const char* tmpBuf = fBuffer; @@ -251,7 +390,11 @@ public: for (size_t i=0; i < fBufferLen; ++i) { if (std::strstr(tmpBuf, strBuf) == nullptr) + { + if (found != nullptr) + *found = true; break; + } --ret; ++tmpBuf; @@ -260,6 +403,17 @@ public: return (ret > fBufferLen) ? fBufferLen : fBufferLen-ret; } + /* + * Clear the string. + */ + void clear() noexcept + { + truncate(0); + } + + /* + * Replace all occurrences of character 'before' with character 'after'. + */ void replace(const char before, const char after) { CARLA_SAFE_ASSERT_RETURN(before != '\0' && after != '\0',); @@ -273,6 +427,9 @@ public: } } + /* + * Truncate the string to size 'n'. + */ void truncate(const size_t n) noexcept { if (n >= fBufferLen) @@ -284,6 +441,9 @@ public: fBufferLen = n; } + /* + * Convert all non-basic characters to '_'. + */ void toBasic() noexcept { for (size_t i=0; i < fBufferLen; ++i) @@ -301,6 +461,9 @@ public: } } + /* + * Convert to all ascii characters to lowercase. + */ void toLower() noexcept { #ifndef BUILD_ANSI_TEST @@ -315,6 +478,9 @@ public: #endif } + /* + * Convert to all ascii characters to uppercase. + */ void toUpper() noexcept { #ifndef BUILD_ANSI_TEST @@ -329,11 +495,17 @@ public: #endif } + /* + * Return a duplicate string buffer. + */ const char* dup() const { return carla_strdup(fBuffer); } + /* + * Direct access to the string buffer. + */ const char* getBuffer() const { return fBuffer; @@ -426,10 +598,14 @@ public: // ------------------------------------------------------------------- private: - char* fBuffer; - size_t fBufferLen; - bool fFirstInit; - + char* fBuffer; // the actual string buffer + size_t fBufferLen; // string length + bool fFirstInit; // true when first initiated + + /* + * Shared init function. + * Called on all constructors. + */ void _init() noexcept { fBuffer = nullptr; @@ -437,8 +613,14 @@ private: fFirstInit = true; } - // allocate string strBuf if not null - // size > 0 only if strBuf is valid + /* + * Helper function. + * Called whenever the string needs to be allocated. + * + * Notes: + * - Allocates string only if first initiated, or 'strBuf' is not null and new string contents are different + * - If 'strBuf' is null 'size' must be 0 + */ void _dup(const char* const strBuf, const size_t size = 0) { if (strBuf != nullptr) @@ -448,7 +630,7 @@ private: { if (! fFirstInit) { - CARLA_ASSERT(fBuffer != nullptr); + CARLA_SAFE_ASSERT(fBuffer != nullptr); delete[] fBuffer; } @@ -464,14 +646,14 @@ private: } else { - CARLA_ASSERT(size == 0); + CARLA_SAFE_ASSERT(size == 0); // don't recreate null string if (fFirstInit || fBufferLen != 0) { if (! fFirstInit) { - CARLA_ASSERT(fBuffer != nullptr); + CARLA_SAFE_ASSERT(fBuffer != nullptr); delete[] fBuffer; } diff --git a/source/utils/CarlaUtils.hpp b/source/utils/CarlaUtils.hpp index 314bac211..73a41f257 100644 --- a/source/utils/CarlaUtils.hpp +++ b/source/utils/CarlaUtils.hpp @@ -74,6 +74,7 @@ void carla_stderr2(const char* const fmt, ...); /* * Print a safe assertion error message. */ +inline void carla_safe_assert(const char* const assertion, const char* const file, const int line) { carla_stderr2("Carla assertion failure: \"%s\" in file %s, line %i", assertion, file, line); @@ -82,6 +83,7 @@ void carla_safe_assert(const char* const assertion, const char* const file, cons /* * Print a safe assertion error message, with 1 extra integer value. */ +inline void carla_safe_assert_int(const char* const assertion, const char* const file, const int line, const int value) { carla_stderr2("Carla assertion failure: \"%s\" in file %s, line %i, value %i", assertion, file, line, value); @@ -90,6 +92,7 @@ void carla_safe_assert_int(const char* const assertion, const char* const file, /* * Print a safe assertion error message, with 2 extra integer values. */ +inline void carla_safe_assert_int2(const char* const assertion, const char* const file, const int line, const int v1, const int v2) { carla_stderr2("Carla assertion failure: \"%s\" in file %s, line %i, v1 %i, v2 %i", assertion, file, line, v1, v2); diff --git a/source/utils/Utils.cpp b/source/utils/Utils.cpp index 230356658..9b3650cfb 100644 --- a/source/utils/Utils.cpp +++ b/source/utils/Utils.cpp @@ -20,6 +20,7 @@ // IDE Helper #ifndef REAL_BUILD # include "CarlaUtils.hpp" +# include "CarlaLibUtils.hpp" #endif #include // needed for va_* @@ -31,9 +32,12 @@ # include // should be included before "windows.h" # include #else +# include # include #endif +// ----------------------------------------------------------------------- + #ifdef CARLA_UTILS_HPP_INCLUDED // ----------------------------------------------------------------------- // string print functions @@ -143,3 +147,67 @@ const char* carla_strdup_free(char* const strBuf) return buffer; } #endif // CARLA_UTILS_HPP_INCLUDED + +// ----------------------------------------------------------------------- + +#ifdef CARLA_LIB_UTILS_HPP_INCLUDED +// ----------------------------------------------------------------------- +// library related calls + +void* lib_open(const char* const filename) +{ + CARLA_SAFE_ASSERT_RETURN(filename != nullptr, nullptr); + +#ifdef CARLA_OS_WIN + return (void*)LoadLibraryA(filename); +#else + return dlopen(filename, RTLD_NOW|RTLD_LOCAL); +#endif +} + +bool lib_close(void* const lib) +{ + CARLA_SAFE_ASSERT_RETURN(lib != nullptr, false); + +#ifdef CARLA_OS_WIN + return FreeLibrary((HMODULE)lib); +#else + return (dlclose(lib) == 0); +#endif +} + +void* lib_symbol(void* const lib, const char* const symbol) +{ + CARLA_SAFE_ASSERT_RETURN(lib != nullptr, nullptr); + CARLA_SAFE_ASSERT_RETURN(symbol != nullptr, nullptr); + +#ifdef CARLA_OS_WIN + return (void*)GetProcAddress((HMODULE)lib, symbol); +#else + return dlsym(lib, symbol); +#endif +} + +const char* lib_error(const char* const filename) +{ + CARLA_SAFE_ASSERT_RETURN(filename != nullptr, nullptr); + +#ifdef CARLA_OS_WIN + static char libError[2048+1]; + carla_zeroChar(libError, 2048+1); + + LPVOID winErrorString; + DWORD winErrorCode = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, winErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&winErrorString, 0, nullptr); + + std::snprintf(libError, 2048, "%s: error code %li: %s", filename, winErrorCode, (const char*)winErrorString); + LocalFree(winErrorString); + + return libError; +#else + return dlerror(); +#endif +} +#endif // CARLA_LIB_UTILS_HPP_INCLUDED + +// -----------------------------------------------------------------------