A better output_iterator

I was using the std::stream_iterator, for example:

    std::vector    vals;
    vals.push_back (12);
    vals.push_back (22);
    vals.push_back (5);
    vals.push_back (30);
    // The std::ostream_iterator appends the ","
    std::ostringstream  str3;
    std::copy (vals.begin(), vals.end(), std::ostream_iterator (str3, ","));
    CPPUNIT_ASSERT_EQUAL (std::string ("12,22,5,30,"), str3.str());

It has the “,” after the last item.
So here’s an implementation that doesn’t append the seperator after the last one:

#ifndef INCLUDED_OUTITER_H
#define INCLUDED_OUTITER_H
#include 
/**
 * A replacement for std::ostream_iterator that doesn't put the
 * seperator after the last item.
 */
template<typename Type, typename CharType = char,
     typename Traits = std::char_traits >
class outiter : public std::iterator
{
public:
    typedef CharType            char_type;
    typedef Traits          traits_type;
    typedef std::basic_ostream    ostream_type;
    /// Initialize from a stream
    outiter (ostream_type &stream)
    : m_stream (&stream),
      m_string (0),
      m_started (false)
    {}
    /// Copy constructor
    outiter (const outiter &copy)
    : m_stream (copy.m_stream),
      m_string (copy.m_string),
      m_started (copy.m_started)
    {}
    /// Initialize from a stream and the seperator
    outiter (ostream_type &stream, const CharType *str)
    : m_stream (&stream),
      m_string (str),
      m_started (false)
    {}
    /// Assignment actually does the output
    outiter &
    operator=(const Type &value)
    {
    if (!m_started)
    {
        m_started = true;
    }
    else if (m_string)
    {
            (*m_stream) << m_string;
    }
    (*m_stream) << value;
    return *this;
    }
    /// Just return a reference to this
    outiter &
    operator*()
    {
    return *this;
    }
    /// Just return a reference to this
    outiter &
    operator++()
    {
    return *this;
    }
    /// Just return a reference to this
    outiter &
    operator++(int)
    {
    return *this;
    }
private:
    /// The stream to write to
    ostream_type *      m_stream;
    /// The seperator (may be NULL)
    const char_type *       m_string;
    /// Flag to indicate if we've output anything
    bool            m_started;
};
#endif /* INCLUDED_OUTITER_H */

And here’s the corresponding code to use it:

    std::ostringstream  str;
    std::copy (vals.begin(), vals.end(), outiter (str, ","));
    CPPUNIT_ASSERT_EQUAL (std::string ("12,22,5,30"), str.str());

Copying a std::vector into a std::map

I had two vector’s with the first being the keys and the second being the values. It took a couple tries before I got the STL working for me!
The first thing was to check if the std::map constructors had something useful. It certainly seems like taking two sets of iterators would be a great way to initialize a map. No such luck.
So how about one of the std algorithms to copy the keys and values into the map? std::copy seemed likely but it only takes a single sequence. A little more digging and std::transform. The second version of std::transform takes two sequences, an output iterator, and a binary function to convert the two values from the two sequences into something that can be inserted into the output iterator. Perfect.
So how to turn the two values into a pair suitable for std::map? The std::make_pair
is exactly what is needed. The hard part is getting the syntax so you can pass it as a function: make_pair in this example.
So the code finally looks like:

#include 
#include 
#include 
#include 
#include 
#include 
void test()
{
    std::map  m;
    std::vector    keys;
    std::vector        values;
    std::transform (keys.begin(), keys.end(),
                    values.begin(),
                    std::inserter (m, m.begin()),
                    std::make_pair);
}

Setting the timezone

I’ve gotten pretty careful about keeping time in UTC and then converting it to localtime for the user to understand. For the first time, I actually had to find the localtime in a non-local timezone. It’s ugly. It seems you have to mess with the TZ environment variable. Here’s what I wrote:

#include 

And here’s a code fragment that uses it:

std::string oldzone = changeTimeZone("US/Pacific");
time_t  seconds = ::time(0);
struct tm   tm_time;
localtime_r (&seconds, tm_time);
changeTimeZone(oldzone);

Expanding $VAR in a string (C++)

Here’s some sample code that expands strings like “$VAR”. There’s a typedef needed and I have it wrapped in a “namespace path” since this code fragment is part of a bigger project. Feel free to use this code however you like.

typedef std::map StringMap
namespace path
{
    /**
     * Expand any $VAR by looking up VAR in vars and using
     * the return value.  If VAR is not found, then the
     * empty string is used.  VAR can contain letters, digits, and
     * underscore ('_') (the usual).  To escape a dollar sign, use two
     * dollar signs ('$$').  If you include the variable name in parenthesis,
     * then any characters are ok: $(A $ %).  '[]' and '{}' work
     * if you need more characters.
     *
     * Variables are recursively expanded.  So if the expansion includes
     * a variable, that variable is also expanded.
     *
     * @param str The String to be expanded
     * @param vars A std::map from std::string to std::string
     * @param tilde True if expand ~ to $HOME (at start)
     * @return a string with all $VARs expanded.
     */
    std::string expand(const std::string &str, const StringMap &vars, bool tilde)
    {
        std::string     newstr;
        const char      intro = '$';
        const char      tilde_char = '~';
        for (std::string::const_iterator iter = str.begin(); iter != str.end();)
        {
            if (tilde)
            {
                tilde = false;  // only at the very start
                if (*iter == tilde_char)
                {
                    ++iter;
                    newstr += expand("$HOME", vars, false);
                    continue;
                }
            }
            // Search for a '$'
            if (*iter != intro)
            {
                newstr += *iter++;
                continue;
            }
            // We have a '$'
            std::string var;
            ++iter;
            // Treat $$ as an escape for a single '$'
            if (iter != str.end() && *iter == intro)
            {
                newstr += *iter++;
                continue;
            }
            // Get the actual variable
            bool start = true;
            bool domatch = false;
            char match = ')'; // for matching brace/parenthesis
            while (iter != str.end())
            {
                if (start)
                {
                    start = false;
                    switch (*iter)
                    {
                        case '(':
                            match = ')';
                            domatch = true;
                            ++iter;
                            continue;
                        case '{':
                            match = '}';
                            domatch = true;
                            ++iter;
                            continue;
                        case '[':
                            match = ']';
                            domatch = true;
                            ++iter;
                            continue;
                        default:
                            break;
                    }
                }
                if (domatch)
                {
                    if (*iter == match)
                    {
                        ++iter;
                        domatch = false;
                        break;
                    }
                    else
                    {
                        var += *iter++;
                    }
                }
                else if (isalnum(*iter) || *iter == '_')
                {
                    var += *iter++;
                }
                else
                {
                    break;
                }
            }
            if (!domatch)
            {
                StringMap::const_iterator variter = vars.find(var);
                // If we added an else, we could have non-matches expand
                if (variter != vars.end())
                    newstr += expand(variter->second, vars, false);
            }
        }
        return newstr;
    }
}