ヘッダーをスキップ
Oracle Coherenceクライアント・ガイド
リリース3.5
B56041-01
  目次
目次

戻る
戻る
 
 

A C++アプリケーションのサンプル

この付録では、consolecontactsおよびhellogridのC++を例にして、サンプル・コードを紹介します。

hellogridのサンプル・コード

hellogridのサンプルを実行した後は、そのコードを調べることをお薦めします。各サンプルには、examplesの下に対応するディレクトリがあります。ここには、サンプル固有のソースが含まれています。また、すべてのサンプルで使用されるソースを含む、commonディレクトリもあります。

例A-1は、hellogrid.cppサンプルのソース・コードで、基本的なキャッシュ・アクセスを示しています。

例A-1 HelloGridサンプル・アプリケーションのコード

#include "coherence/lang.ns"

#include "coherence/net/cache/ContinuousQueryCache.hpp"
include "coherence/net/CacheFactory.hpp"
#include "coherence/net/NamedCache.hpp"
#include "coherence/stl/boxing_map.hpp"
#include "coherence/util/aggregator/ComparableMin.hpp"
#include "coherence/util/extractor/IdentityExtractor.hpp"
#include "coherence/util/filter/GreaterFilter.hpp"
#include "coherence/util/processor/NumberIncrementor.hpp"
#include "coherence/util/Iterator.hpp"
#include "coherence/util/Filter.hpp"
#include "coherence/util/Set.hpp"
#include "coherence/util/ValueExtractor.hpp"
#include "coherence/util/ValueManipulator.hpp"

#include "VerboseMapListener.hpp"

#include <iostream>

using namespace coherence::lang;

using coherence::examples::VerboseMapListener;
using coherence::net::cache::ContinuousQueryCache;
sing coherence::net::CacheFactory;
using coherence::net::NamedCache;
using coherence::stl::boxing_map;
using coherence::util::aggregator::ComparableMin;
using coherence::util::extractor::IdentityExtractor;
using coherence::util::filter::GreaterFilter;
using coherence::util::processor::NumberIncrementor;
using coherence::util::Iterator;
using coherence::util::Filter;
using coherence::util::Set;
using coherence::util::ValueExtractor;
using coherence::util::ValueManipulator;


/**
* This example demonstrates the basics of accessing a cache by using the
* Coherence C++ API.
*
* @argc  the number of command line arguments (including the process name)
* @argv  [cache-name]
*/
int main(int argc, char** argv)
    {
    try
        {
        // read optional cache name from command line
        String::View vsCacheName = argc > 1 ? argv[1] : "dist-hello";

        // retrieve the named cache
        NamedCache::Handle hCache = CacheFactory::getCache(vsCacheName);
        std::cout << "retrieved cache \"" << hCache->getCacheName()
                  << "\" containing " << hCache->size() << " entries"
                  << std::endl;

        // create a key, and value
        String::View vsKey   = "hello";
        String::View vsValue = "grid";

        // insert the pair into the cache
        hCache->put(vsKey, vsValue);
        std::cout << "\tput: " << vsKey << " = " << vsValue << std::endl;

        // read back the value, casting to the expected value type
        String::View vsGet = cast<String::View>(hCache->get(vsKey));
        std::cout << "\tget: " << vsKey << " = " << vsGet << std::endl;

        // read a non-existent entry from the cache; result will be NULL
        String::View vsKeyDummy = "dummy";
        Object::View vDummy     = hCache->get(vsKeyDummy);
        std::cout << "\tget: " << vsKeyDummy << " = " << vDummy << std::endl;

        // work with non-string data types
        hCache->put(Integer32::valueOf(12345), Float64::valueOf(6.7));
        hCache->put(Integer32::valueOf(23456), Float64::valueOf(7.8));
        hCache->put(Integer32::valueOf(34567), Float64::valueOf(8.9));

        // iterate and print the cache contents, treating contents abstractly
        std::cout << "entire cache contents:" << std::endl;
        for (Iterator::Handle hIter = hCache->entrySet()->iterator();
             hIter->hasNext(); )
            {
            Map::Entry::View vEntry = cast<Map::Entry::View>(hIter->next());
            Object::View     vKey   = vEntry->getKey();
            Object::View     vValue = vEntry->getValue();
            std::cout << '\t' << vKey << " = " << vValue << std::endl;
            }

        // remove strings to make the cache contents uniform
        hCache->remove(vsKey);

        // caches may also be wrapped with an STL-like map adapter
        typedef boxing_map<Integer32, Float64> float_cache;
        float_cache cache(hCache);
        cache[45678] = 9.1;
        std::cout << "updated cache contents:" << std::endl;
        for (float_cache::iterator i = cache.begin(), e = cache.end(); i != e;++i)
            {
            std::cout << '\t' << i->first << " = " << i->second << std::endl;
            }

        // perform aggregation, and print the results
        ValueExtractor::View vExtractor = IdentityExtractor::getInstance();
        Float64::View        vFlMin     = cast<Float64::View>(
                hCache->aggregate((Filter::View) NULL,
                ComparableMin::create(vExtractor)));
        std::cout << "minimum: " << vFlMin << std::endl;

        // query the cache, and print the results
        Filter::View vFilter    = GreaterFilter::create(vExtractor,
                                                  Float64::valueOf(7.0));
        Set::View    vSetResult = hCache->entrySet(vFilter);

        std::cout << "filtered cache contents by " << vFilter << std::endl;
        for (Iterator::Handle hIter = vSetResult->iterator(); hIter->hasNext(); )
            {
            Map::Entry::View vEntry = cast<Map::Entry::View>(hIter->next());
            Object::View     vKey   = vEntry->getKey();
            Object::View     vValue = vEntry->getValue();
            std::cout << '\t' << vKey << " = " << vValue << std::endl;
            }

        // present a real-time filtered view of the cache
        NamedCache::Handle hCacheCqc =
            ContinuousQueryCache::create(hCache, vFilter);
        std::cout << "ContinuousQueryCache filtered view: " << std::endl;
        for (Iterator::Handle hIter = hCacheCqc->entrySet()->iterator();
             hIter->hasNext(); )
            {
            Map::Entry::View vEntry = cast<Map::Entry::View>(hIter->next());
            Object::View     vKey   = vEntry->getKey();
            Object::View     vValue = vEntry->getValue();
            std::cout << '\t' << vKey << " = " << vValue << std::endl;
            }

        // register MapListener to print changes to stdout
        std::cout << "start listening to events..." << std::endl;
        hCache->addFilterListener(VerboseMapListener::create());

        // invoke entry processor on matchingcache contents, incrementing each
        // value by the minimum value
        Float64::Handle vFlIncr = Float64::valueOf(1.0);
        std::cout << "increment results by " << vFlIncr << std::endl;
        hCacheCqc->invokeAll((Filter::View) NULL, NumberIncrementor::create(
                (ValueManipulator::View) NULL, vFlIncr, /*fPost*/ true));

        // disconnect from the grid
        CacheFactory::shutdown();
        }
    catch (const std::exception& e)
        {
        std::cerr << "error: " << e.what() << std::endl;
        return 1;
        }
    return 0;
    }

consoleのサンプル・コード

consoleのサンプルを実行した後は、そのコードを調べることをお薦めします。各サンプルには、examplesの下に対応するディレクトリがあります。ここには、サンプル固有のソースが含まれています。また、すべてのサンプルで使用されるソースを含む、commonディレクトリもあります。

例A-2は、簡単なコマンドでキャッシュとの対話を可能にする、console.cppコマンドライン・アプリケーションのソース・コードを示しています。

例A-2 Consoleサンプル・アプリケーションのコード

#include "coherence/lang.ns"

#include "coherence/io/pof/SystemPofContext.hpp"
#include "coherence/net/CacheFactory.hpp"
#include "coherence/net/NamedCache.hpp"
#include "coherence/util/Iterator.hpp"
#include "coherence/util/Map.hpp"
#include "coherence/util/Set.hpp"

#include "StreamParser.hpp"

#include <iostream>
#include <sstream>

using namespace coherence::lang;

using coherence::examples::StreamParser;
using coherence::io::pof::SystemPofContext;
using coherence::net::CacheFactory;
using coherence::net::NamedCache;
using coherence::util::Iterator;
using coherence::util::Map;
using coherence::util::Set;


/**
* This Coherence for C++ example provides a simple console for playing with
* caches from within C++.
*
* @argc  the number of command line arguments (including the process name)
* @argv  [cache-name]
*/
int main(int argc, char** argv)
    {
    NamedCache::Handle hCache;

    if (argc > 1)
        {
        // load command line specified cache
        try
            {
            hCache = CacheFactory::getCache(argv[1]);
            }
        catch (const std::exception& e)
            {
            std::cerr << e.what() << std::endl;
            }
        }

    while (true)
        {
        try
            {
            // prompt for input
            std::cout << "\nMap (";
            if (NULL == hCache)
                {
                std::cout << '?';
                }
            else
                {
                std::cout << hCache->getCacheName();
                }
            std::cout << "): " << std::flush;

            char achInput[256];
            std::cin.getline(achInput, 256);

            if (std::cin.fail())
                {
                std::cin.clear();
                continue;
                }

            std::stringstream ssInput(achInput);

            // process input
            String::View vsCmd = cast<String::View>(StreamParser::next(ssInput));

            if (vsCmd->equals("bye"))
                {
                // quit
                try
                    {
                    CacheFactory::shutdown();
                    }
                catch (const std::exception& e)
                    {
                    std::cerr << e.what() << std::endl;
                    return 1;
                    }
                return 0;
                }
            else if (vsCmd->equals("cache"))
                {
                // lookup a cache from the CacheFactory
                String::View vsCacheName = cast<String::View>(StreamParser::next(ssInput));
                hCache = CacheFactory::getCache(vsCacheName);
                }
            else if (vsCmd->equals("classes"))
                {
                // output the SystemClassLoader
                std::cout << SystemClassLoader::getInstance() << std::endl;
                }
            else if (vsCmd->equals("clear"))
                {
                // clear the current cache
                hCache->clear();
                }
            else if (vsCmd->equals("destroy"))
                {
                // destroy the current cache
                CacheFactory::destroyCache(hCache);
                }
            else if (vsCmd->equals("get"))
                {
                // perform a get operation on the current cache
                Object::View vKey   = StreamParser::next(ssInput);
                Object::View vValue = hCache->get(vKey);

                // print the current value
                std::cout << vValue << std::endl;
                }
            else if (vsCmd->equals("list"))
                {
                // obtain the entire cache contents
                Set::View vSetEntries = hCache->entrySet();

                // print key value pairs
                for (Iterator::Handle hIter = vSetEntries->iterator();
                        hIter->hasNext(); )
                    {
                    Map::Entry::View vEntry =
                        cast<Map::Entry::View>(hIter->next());

                    std::cout << vEntry->getKey() << " = "
                              << vEntry->getValue() << std::endl;
                    }
                }
            else if (vsCmd->equals("memory"))
                {
                // print information about allocated objects
                HeapAnalyzer::View vAnalyzer = System::getHeapAnalyzer();

                if (NULL == vAnalyzer)
                    {
                    std::cout << "analysis disabled" << std::endl;
                    }
                else if (cast<String::View>(StreamParser::next(ssInput))->
                        equals("delta"))
                    {
                    static HeapAnalyzer::Snapshot::View vMark;

                    // compare current against the heap mark
                    std::cout << (NULL == vMark
                            ? vAnalyzer->capture() : vAnalyzer->delta(vMark))
                            << std::endl;

                    // reset to mark based on the current heap useage
                    vMark = NULL;
                    vMark = vAnalyzer->capture();
                    }
                else
                    {
                    // output the current heap useage
                    std::cout << vAnalyzer << std::endl;
                    }
                }
            else if (vsCmd->equals("pof"))
                {
                // output the SystemPofContext
                std::cout << SystemPofContext::getInstance() << std::endl;
                }
            else if (vsCmd->equals("put"))
                {
                // perform a put operation on the current cache
                Object::View vKey   = StreamParser::next(ssInput);
                Object::View vValue = StreamParser::next(ssInput);
                Object::View vPrev  = hCache->put(vKey, vValue);

                // print the old value
                std::cout << vPrev << std::endl;
                }
            else if (vsCmd->equals("remove"))
                {
                // perform a remove operation on the current cache
                Object::View vKey  = StreamParser::next(ssInput);
                Object::View vPrev = hCache->remove(vKey);

                // print the removed value
                std::cout << vPrev << std::endl;
                }
            else if (vsCmd->equals("release"))
                {
                // release the current cache
                CacheFactory::releaseCache(hCache);
                }
            else if (vsCmd->equals("size"))
                {
                // print the size of the current cache
                size32_t cElements = hCache->size();

                std::cout << cElements << std::endl;
                }
            else if (vsCmd->equals("threads"))
                {
                // print a stack trace for all threads related to coherence
                Thread::dumpStacks(std::cout);
                }
            else if (vsCmd->equals(""))
                {
                continue;
                }
            else if (vsCmd->equals("help"))
                {
                // print help
                std::cout << "The commands are:"
                    << std::endl << "  bye"
                    << std::endl << "  cache <name>"
                    << std::endl << "  classes"
                    << std::endl << "  clear"
                    << std::endl << "  destroy"
                    << std::endl << "  get <key>"
                    << std::endl << "  help"
                    << std::endl << "  list"
                    << std::endl << "  memory [delta]"
                    << std::endl << "  pof"
                    << std::endl << "  put <key> <value>"
                    << std::endl << "  release"
                    << std::endl << "  remove <key>"
                    << std::endl << "  size"
                    << std::endl << "  threads"
                    << std::endl;
                }
            else
                {
                std::cout << "Unknown command: \"" << vsCmd << "\"\n"
                    << "Entry \"help\" for command list" << std::endl;
                }
            }
        catch (const NullPointerException::Throwable& e)
            {
            if (NULL == hCache)
                {
                std::cerr << "Please specify a cache using the \"cache\" "
                          << "command." << std::endl;
                }
            else
                {
                std::cerr << e << std::endl;
                }
            }
        catch (const std::exception& e)
            {
            std::cerr << "Error: " << e.what() << std::endl;
            }
        }
    }

contactsのサンプル・コード

contactsのサンプルを実行した後は、そのコードを調べることをお薦めします。各サンプルには、examplesの下に対応するディレクトリがあります。ここには、サンプル固有のソースが含まれています。また、すべてのサンプルで使用されるソースを含む、commonディレクトリもあります。

例A-3は、contactsサンプルのソース・コードで、既存の(つまり、Coherenceではない)C++クラスをグリッド内に格納する方法を示しています。

ContactInfo.hpp

例A-3 ContactInfoサンプル・アプリケーションのヘッダー・コード

#ifndef COH_EXAMPLES_CONTACT_INFO_HPP
#define COH_EXAMPLES_CONTACT_INFO_HPP

#include <ostream>
#include <string>

/**
* The ContactInfo class encapsulates common contact information for a person.
*
* This serves as an example data object which does not have direct knowledge
* of Coherence but can be stored in the data grid.
*/
class ContactInfo
    {
    // ----- constructors ---------------------------------------------------

    public:
        /**
        * Create a new ContactInfo object.
        *
        * @param sName    the name of the person
        * @param sStreet  the street on which the person lives
        * @param sCity    the city where the person lives
        * @param sState   the state where the person lives
        * @param sZip     the zip code of the city where the person lives
        */
        ContactInfo(const std::string& sName,
                const std::string& sStreet, const std::string& sCity,
                const std::string& sState, const std::string& sZip);

        /**
        * Copy constructor.
        */
        ContactInfo(const ContactInfo& that);

    protected:
        /**
        * Default constructor.
        */
        ContactInfo();


    // ----- accessors ------------------------------------------------------

    public:
        /**
        * Determine the name of the person for which this ContactInfo object
        * contains contact information.
        *
        * @return the person's name
        */
        std::string getName() const;

        /**
        * Configure the name of the person for which this ContactInfo object
        * contains contact information.
        *
        * @param sName  the person's name
        */
        void setName(const std::string& sName);

        /**
        * Determine the street on which the person lives.
        *
        * @return the street name
        */
        std::string getStreet() const;

        /**
        * Configure the street on which the person lives.
        *
        * @param sStreet  the street name
        */
        void setStreet(const std::string& sStreet);

        /**
        * Determine the city in which the person lives.
        *
        * @return the city name
        */
        std::string getCity() const;

        /**
        * Configure the city in which the person lives.
        *
        * @param sCity  the city name
        */
        void setCity(const std::string& sCity);

        /**
        * Determine the state in which the person lives.
        *
        * @return the state name
        */
        std::string getState() const;

        /**
        * Configure the state in which the person lives.
        *
        * @param sState  the state name
        */
        void setState(const std::string& sState);

        /**
        * Determine the zip code of the city in which the person lives.
        *
        * @return the zip code
        */
        std::string getZip() const;

        /**
        * Configure the zip code of the city in which the person lives.
        *
        * @param sZip  the city's zip code
        */
        void setZip(const std::string& sZip);

    // ----- data members ---------------------------------------------------

    private:
        /**
        * The person's name.
        */
        std::string m_sName;

        /**
        * The street on which the person lives.
        */
        std::string m_sStreet;

        /**
        * The city in which the person lives.
        */
        std::string m_sCity;

        /**
        * The state in which the person lives.
        */
        std::string m_sState;

        /**
        * The zip code of the city in which the person lives.
        */
        std::string m_sZip;
    };

// ----- free functions -----------------------------------------------------

/**
* Output this ContactInfo to the stream
*
* @param out  the stream to output to
*
* @return the stream
*/
std::ostream& operator<<(std::ostream& out, const ContactInfo& info);

/*** Perform an equality test on two ContactInfo objects** @param infoA the first ContactInfo* @param infoB the second ContactInfo** @return true if the objects are equal*/bool operator==(const ContactInfo& infoA, const ContactInfo& infoB); /*** Return the has for the ContactInfo.** @param info the ContactInfo to hash** @return the hash for the ContactInfo*/size_t hash_value(const ContactInfo& info);

#endif // COH_EXAMPLES_CONTACT_INFO_HPP

ContactInfo.cpp

例A-4 ContactInfoサンプル・アプリケーションのC++コード

#include "ContactInfo.hpp"


// ----- constructors -------------------------------------------------------

ContactInfo::ContactInfo(const std::string& sName,
        const std::string& sStreet, const std::string& sCity,
        const std::string& sState, const std::string& sZip)
    {
    setName(sName);
    setStreet(sStreet);
    setCity(sCity);
    setState(sState);
    setZip(sZip);
    }

ContactInfo::ContactInfo(const ContactInfo& that)
    {
    setName(that.getName());
    setStreet(that.getStreet());
    setCity(that.getCity());
    setState(that.getState());
    setZip(that.getZip());
    }

ContactInfo::ContactInfo()
    {
    }

// ----- accessors ----------------------------------------------------------

std::string ContactInfo::getName() const
    {
    return m_sName;
    }

void ContactInfo::setName(const std::string& sName)
    {
    m_sName = sName;
    }

std::string ContactInfo::getStreet() const
    {
    return m_sStreet;
    }

void ContactInfo::setStreet(const std::string& sStreet)
    {
    m_sStreet = sStreet;
    }

std::string ContactInfo::getCity() const
    {
    return m_sCity;
    }

void ContactInfo::setCity(const std::string& sCity)
    {
    m_sCity = sCity;
    }

std::string ContactInfo::getState() const
    {
    return m_sState;
    }

void ContactInfo::setState(const std::string& sState)
    {
    m_sState = sState;
    }

std::string ContactInfo::getZip() const
    {
    return m_sZip;
    }

void ContactInfo::setZip(const std::string& sZip)
    {
    m_sZip = sZip;
    }

// ----- free functions -----------------------------------------------------

std::ostream& operator<<(std::ostream& out, const ContactInfo& info)
    {
    out << "ContactInfo("
            << "Name="     << info.getName()
            << ", Street=" << info.getStreet()
            << ", City="   << info.getCity()
            << ", State="  << info.getState()
            << ", Zip="    << info.getZip()
            << ')';
    return out;
    }

bool operator==(const ContactInfo& infoA, const ContactInfo& infoB)
    {
    return infoA.getName()   == infoB.getName()   &&
           infoA.getStreet() == infoB.getStreet() &&
           infoA.getCity()   == infoB.getCity()   &&
           infoA.getState()  == infoB.getState()  &&
           infoA.getZip()    == infoB.getZip();
    }

size_t hash_value(const ContactInfo& info)
    {
    return size_t(&info); // identity hash (note: not suitable for cache keys)
    }

ContactInfoSerializer.cpp

例A-5 PortableContactInfoアプリケーションのC++コード

/**
* This file defines serializers for the ContactInfo class and registers its
* managed form (Managed<ContactInfo>) with Coherence.
*/

#include "coherence/lang.ns"

#include "coherence/io/pof/PofWriter.hpp"
#include "coherence/io/pof/PofReader.hpp"
#include "coherence/io/pof/SystemPofContext.hpp"
#include "ContactInfo.hpp"

#include <string>

using namespace coherence::lang;

using coherence::io::pof::PofReader;
using coherence::io::pof::PofWriter;

COH_REGISTER_MANAGED_CLASS(1001, ContactInfo);

template<> void serialize<ContactInfo>(PofWriter::Handle hOut, const ContactInfo& info)
    {
    hOut->writeString(0, info.getName());
    hOut->writeString(1, info.getStreet());
    hOut->writeString(2, info.getCity());
    hOut->writeString(3, info.getState());
    hOut->writeString(4, info.getZip());
    }

template<> ContactInfo deserialize<ContactInfo>(PofReader::Handle hIn)
   {
    std::string sName   = hIn->readString(0);
    std::string sStreet = hIn->readString(1);
    std::string sCity   = hIn->readString(2);
    std::string sState  = hIn->readString(3);
    std::string sZip    = hIn->readString(4);
    return ContactInfo(sName, sStreet, sCity, sState, sZip);
    }

contacts.cpp

例A-6 ContactInfoデータ・オブジェクトのコード

#include "coherence/lang.ns"

#include "coherence/net/CacheFactory.hpp"
#include "coherence/net/NamedCache.hpp"

#include "ContactInfo.hpp"

#include <iostream>
#include <sstream>

using namespace coherence::lang;

using coherence::net::CacheFactory;
using coherence::net::NamedCache;

// ----- function prototypes ------------------------------------------------

/**
* Create a contact from stdin.
*
* @return the contact
*/
ContactInfo readContact();

/**
* This Coherence for C++ example illustrates how to use non-Coherence data
* objects in the grid. This example operates on the ContactInfo class which
* is not Coherence aware.
*
* To run this against a remote cache, the proxy node must have the
* corresponding Java ContactInfo.class in its classpath.
*
* @argc  the number of command line arguments (including the process name)
* @argv  [cache-name]
*/
int main(int argc, char** argv)
    {
    try
        {
        String::View       vsCacheName = argc > 1 ? argv[1] : "dist-contacts";
        NamedCache::Handle hCache      = CacheFactory::getCache(vsCacheName);

        while (true)
            {
            // prompt for input
            std::cout << "contacts> " << std::flush;

            char achInput[256];
            std::cin.getline(achInput, 256);
            if (std::cin.fail())
                {
                std::cin.clear();
                continue;
                }

            std::stringstream ssInput(achInput);

            // process input
            std::string sCmd;
            ssInput >> sCmd;

            if (sCmd == "")
                {
                continue;
                }
            else if (sCmd == "bye")
                {
                // quit
                CacheFactory::shutdown();
                return 0;
                }
            else if (sCmd == "create")
                {
                ContactInfo ci = readContact();
                std::cout << "storing: " << ci << std::endl;
                hCache->put(String::create(ci.getName().c_str()),
                        Managed<ContactInfo>::create(ci));
                }
            else if (sCmd == "find")
                {
                std::string sPart;
                ssInput >> sPart;

                std::cout << "Name: " << std::flush;
                std::cin.getline(achInput, 256);
                String::View vsName = achInput;

               Managed<ContactInfo>::View vInfo =
                    cast<Managed<ContactInfo>::View>(hCache->get(vsName));

                if (NULL == vInfo)
                    {
                    std::cout << vsName << " not found" << std::endl;
                    continue;
                    }

                if (sPart == "" || sPart == "all")
                    {
                    std::cout << vInfo << std::endl;
                    }
                else if (sPart == "street")
                    {
                    std::cout << vInfo->getStreet() << std::endl;
                    }
                else if (sPart == "city")
                    {
                    std::cout << vInfo->getCity() << std::endl;
                    }
                else if (sPart == "state")
                    {
                    std::cout << vInfo->getState() << std::endl;
                    }
                else if (sPart == "zip")
                    {
                    std::cout << vInfo->getZip() << std::endl;
                    }
                else
                    {
                    std::cerr << "find must be followed by, street, city, "
                        << "state, or zip" << std::endl;
                    }
                }
            else // output help
                {
                std::cout << "commands are:"
                    << std::endl << "bye"
                    << std::endl << "create"
                    << std::endl << "find <street | city | state | zip | all>"
                    << std::endl;
                }
            }
        }
    catch (const std::exception& e)
        {
        std::cerr << e.what() << std::endl;
        }
    }

ContactInfo readContact()
    {
    char achInput[256];
    std::cout << "Name: " << std::flush;
    std::cin.getline(achInput, 256);
    std::string sName(achInput);

    std::cout << "Street: " << std::flush;
    std::cin.getline(achInput, 256);
    std::string sStreet(achInput);

    std::cout << "City: " << std::flush;
    std::cin.getline(achInput, 256);
    std::string sCity(achInput);

    std::cout << "State: " << std::flush;
    std::cin.getline(achInput, 256);
    std::string sState(achInput);

    std::cout << "Zip: " << std::flush;
    std::cin.getline(achInput, 256);
    std::string sZip(achInput);

    return ContactInfo(sName, sStreet, sCity, sState, sZip);
    }