C++ Code Snippets 1

This is a set of some code snippets that you might find useful.

The 1st one is pretty popular, but I use it in the snippets below. Basically it is

A way to retrieve the size of a C++ array.

template <typename T, size_t N> char (&TArrSize_Safe(T (&)[N]))[N];
#define ARRSZ(A) (sizeof(TArrSize_Safe(A)))

// Usage
int myArray[1234];
printf("myArray has %d elements!" ARRSZ(myArray));

printf style std::string formatting:

// The caller is EXPECTED to call va_end on the va_list args
inline void string_format(std::string& retval, const char* const fmt_str, va_list args)
{   
    // [CAUTION]
    // Under Windows with msvc it is fine to call va_start once and then use the va_list multiple times.
    // However this is not the case on the other platforms. The POSIX docs state that the va_list is undefined
    // after calling vsnprintf with it:
    //
    // From https://linux.die.net/man/3/vsprintf :
    // The functions vprintf(), vfprintf(), vsprintf(), vsnprintf() are equivalent to the functions 
    // printf(), fprintf(), sprintf(), snprintf(), respectively, 
    // except that they are called with a va_list instead of a variable number of arguments. 
    // These functions do not call the va_end macro. Because they invoke the va_arg macro, 
    // the value of ap is undefined after the call. See stdarg(3). 
    // Obtain the length of the result string.
    va_list args_copy;
    va_copy(args_copy, args);
    
    const size_t ln = vsnprintf(nullptr, 0, fmt_str, args);

    // [CAUTION] Write the data do the result. Allocate one more element 
    // as the *sprintf function add the '\0' always. Later we will pop that element.
    retval.resize(ln + 1, 'a');

    vsnprintf(&retval[0], retval.size()+1, fmt_str, args_copy);
    retval.pop_back(); // remove the '\0' that was added by snprintf on the back.
    va_end(args_copy);
}

inline void string_format(std::string& retval, const char* const fmt_str, ...)
{
    va_list args;
    va_start(args, fmt_str);
    string_format(retval, fmt_str, args);
    va_end(args);
}

inline std::string string_format(const char* const fmt_str, ...)
{
    std::string retval;
    
    va_list args;
    va_start(args, fmt_str);
    string_format(retval, fmt_str, args);
    va_end(args);

    return retval;
}

Simple file open dialog.

WINAPI is used for Windows, and zenity for GNU/Linux:

template <typename T, size_t N> char (&TArrSize_Safe(T (&)[N]))[N];
#define ARRSZ(A) (sizeof(TArrSize_Safe(A)))


std::string FileOpenDialog(const std::string& prompt)
{
#ifdef WIN32

    static std::mutex mtx;
    std::lock_guard<std::mutex> mtx_guard(mtx);

    const int BUFSIZE = 1024;
    char buffer[BUFSIZE] = {0};

    TCHAR currentDir[MAX_PATH];

    // GetOpenFileName changes the current directory. We do not want that so we revert it back to what it was.
    if(GetCurrentDirectory(ARRAYSIZE(currentDir), currentDir)!=0)
    {
        OPENFILENAME ofns = {0};
        ofns.lStructSize = sizeof(ofns);
        ofns.lpstrFile = buffer;
        ofns.nMaxFile = BUFSIZE;
        ofns.lpstrTitle = prompt.c_str();
        ofns.lpstrFilter = fileFilter ? fileFilter : "All Files\0*.*\0";
        ofns.Flags |= OFN_FILEMUSTEXIST;
        
        const BOOL okClicked  = GetOpenFileNameA(&ofns);
        SetCurrentDirectory(currentDir);
    }
    else
    {
        assert(false); // Should never happen!
    }

    return std::string(buffer);
#else
    char file[1024] = {0};
    FILE* const f = popen("zenity --file-selection", "r");
    assert(f!=nullptr);
    fgets(file, 1024, f);
    file[ARRSZ(file)] = '\0'; // Clamp if the filename is too long.
    std::string s(file);
    
    // Usually there is a newline symbol, if so remove it.
    if(s.size() > 0 && s.back() == '\n') {
        s.pop_back();
    }
    
    pclose(f);
    return s;
#endif
}

A std::chrono based timer:

struct Timer
{
    typedef std::chrono::high_resolution_clock clock;
    typedef clock::time_point time_point;

    // The one below is set in a *.cpp to this:
    // const Timer::time_point Timer::application_start_time = Timer::clock::now();
    static const time_point application_start_time;

    Timer()
    {
        reset();
    }

    void reset()
    {
        lastUpdate = clock::now();
        dtSeconds = 0.f;
    }

    // Returns the current time since the application start in seconds.
    static float now_seconds()
    {
        return std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - application_start_time).count() * 1e-6f;
    }

    void tick()
    {
        const time_point now = clock::now();
        const auto diff = std::chrono::duration_cast<std::chrono::microseconds>(now - lastUpdate).count();
        lastUpdate = now;

        dtSeconds = diff * 1e-6f; // microseconds -> seconds
    }

    float diff_seconds() const { return dtSeconds; }

private :

    time_point lastUpdate;
    float dtSeconds;
};
comments powered by Disqus