This project is read-only.

Windows via C++ (WCP) 1.0.2.1056 (Beta 3)

WCP is an acronynim for the Windows via C++ library. The purpose of the library is to take advantage of C++ programming language when you write a program in Windows API using Microsoft Visual Studio 2005 or higher, The Windows API has commonly C interface so Windows developers often face with cross-language issues and are forced to refuse most of high-level language benefits. You may argue the relevance of this library, because there are a huge amount of work done in this scope: MFC, ATL, WTL, Qt, wxWidgets and other very popular C++ libraries. What can provide such a small library as WCP?

Years of my experience of writing programs using different libraries proved the strong need in having some set of classes and tools that I could use in any project, regardless what libraries it uses. With a course of time I have collected a big number of commonly used classes and tools and decided to organize my work in a single and universal library.

Here are some arguments that prove a need in WCP:

Smart handles

Whatever library you use, if you program requires intensive usage of Windows API you are forced to use this API directly because no one library would provide you a set of classes for all cases. For example, if you need to call a system function that returns a pointer to HLOCAL (which requires a call to LocalFree), neither library allow you to release this memory automatically, and you are forced to insert bunch of try...catch blocks, to track a pointer all across a module in order to avoid memory leaks. It's too much work. And it's now a secrect that redundant work is usually error prone and increase program complexity in times.
WCP provides smart handles for almost user-level Windows handles, that release resources on destruction. Here is an example of HLOCAL smart handle using:

     // This is what all Windows developers are used to
    HANDLE hMutex = CreateMutex(NULL, TRUE, NULL);
    if(hMutex != NULL)
    {
        try
        {
            // Here goes a code that may throw any exception
        }
        catch(...)
        {
            CloseHandle(hMutex);
            hMutex = NULL;
            throw;
        }
    }
Too much code, isn't it? Wouldn't it be much easier to write something like this:

    wcp::handles::handle_t hMutex = CreateMutex(NULL, TRUE, NULL);
    if(hMutex)
    {
        // Here goes a code that may throw any exception.
        // Note: No CloseHandle is needed any more.
    }
Look at the UserAccountInfo.cpp example to start off with WCP smart handles.

Advanced synchronization

Second argument is about synchronization. synchronization is always a big issue. Using WCP you can achive almost transparent, but at the same time simple and reliable synchronzation:
  • Derive classes from wcp::synchronizable<T> to be able to synchronize object access using wcp::synchronizable::sync method or LOCK[n] statements (C#-like syntax of object synchronization).
    class Person : public wcp::synchronizable<Person>
    {
        std::string m_name;
    public:
        const std::string& getName() const
        { return m_name; }
  
        void setName(const char* name)
        { m_name.assign(name); }
    };
     
    Person person;
  
    void do_test()
    {
        person.sync()->setName("Jack"); // This line of code will be executed synchronously.
  
        // Is identical to
  
        LOCK(person) // This code will be executed synchronously too.
        {
            person.setName("Jack");
        }
    }
  • Use and create new synchronization objects derived from wcp::sync_object and perform synchronization using GUARD[n] macros.
    wcp::critical_section cs;
    GUARD(cs)
    {
        ...
    }

    // or guard two or more syncrhonizable objects simultaneosly
    wcp::monitor monitor;
    GUARD2(monitor, cs)
    {
        ...
    }
  • Use SYNCHRONIZED statement to achive simple in-place syncronization:
     SYNCHRONIZED
     {
         // Critical code goes here. Only one thread can execute this code simultaneously.
     }
  • Take advantage of the wcp::syncronized object wrapper:
     wcp::synchronized<std::string> name;
     LOCK(name)
     {
         *name = "John";
     }
  
     // or even much simpler
     name.sync()->assign("John");

Advanced debugging

The third argument, is advanced debugging capabilities. If you use OutputDebugString to print debug messages, apparently you got tired of writing formatting code wherever you call OutputDebugString. In the best case you have a function that takes a variable number of arguments, like printf. WCP provides you DBGM, DBGF, DBGL and DBGE to print respectively any message, calling function name, line information and error message. Furthermore, defining a WCP_DEBUG_XML macro enforces WCP to print out debug information in XML format so that you can save debug output as an XML file and apply an XSLT transformation to it to get a formatted HTML report about your program workflow. C++-stream-like interface gives you lots of flexible formatting options.

     // Compile with: /D "WCP_DEBUG_XML"
  
     wcp::uint_t fibonachi(wcp::uint_t v)
     {
         DBGM("HEX Fibonachi: 0x" <<std::hex <<std::setw(8) <<std::setfill(TEXT('0')) <<v);
         return v < 2 ? 1 : (fibonachi(v - 1) + fibonachi(v - 2));
     }
  
     void main()
     {
         DBGF();
  
         std::tcout <<fibonachi(4);
  
         DBGL();
     }
debug output is:

     <WCPDebugLog wcp-version="1.0">
     <Debug function="main2" line="47" file="d:\devel\root\devel\wcp\wcp.cpp" />
     <Message><![CDATA[ HEX Fibonachi: 0x00000004 ]]></Message>
     <Message><![CDATA[ HEX Fibonachi: 0x00000003 ]]></Message>
     <Message><![CDATA[ HEX Fibonachi: 0x00000002 ]]></Message>
     <Message><![CDATA[ HEX Fibonachi: 0x00000001 ]]></Message>
     <Message><![CDATA[ HEX Fibonachi: 0x00000000 ]]></Message>
     <Message><![CDATA[ HEX Fibonachi: 0x00000001 ]]></Message>
     <Message><![CDATA[ HEX Fibonachi: 0x00000002 ]]></Message>
     <Message><![CDATA[ HEX Fibonachi: 0x00000001 ]]></Message>
     <Message><![CDATA[ HEX Fibonachi: 0x00000000 ]]></Message>
     <Debug line="51" file="d:\devel\root\devel\wcp\wcp.cpp" />
     </WCPDebugLog>

Type constraints

The next, the fifth argument, regards C++ language extenstion. Besides GUARD[n], LOCK[n] and SYNCHRONIZED statements WCP also provides tremendously flexible and exandable type constraints which allow you to prevent up to 90% of errors caused by "wrong" template type. Using WCP type constraints you can check up to 19 common type traits, you can perform logic operations over constraints checks, you can create your new type constraint templates, etc. The very good example to start of is Serialization.cpp.

     // C# example of generic type constraint.
     using System;
     public class MyGenericClass <T> where T: IComparable, IClonable
     { ... }
and this is a small part or what WCP type constraints allow you to do:

     template<class T>
     class Serializable
     { 
        // A type constrained to be either POD or integral 
        // and to have at least one data member.
        WCP_ENSURE_TYPE_CONSTRAINT(
            wcp::constraint<T, 
                wcp::and_<
                    wcp::or_<
                        wcp::has_traits_<wcp::is_pod>, 
                        wcp::has_traits_<wcp::is_integral> >,
                    wcp::not_<wcp::has_traits_<wcp::is_empty> > 
                > 
            > IS_MAINTAINED);
         ...
     };

Thus, WCP has the following type constraints:
  1. wcp::has_traits_ - checks a type traits defined by wcp::type_traits.
  2. wcp::is_base_of_<T> - checks if a checked type is base for a type specified.
  3. wcp::is_derived_from_<T> - checks if a checked type is derived from a type specified.
  4. wcp::is_convertible_to_<T> - checks if a checked type is convertible to a type specified.
  5. wcp::is_equal_to_<T> - checks if a checked type is the same as a type specified.
  6. wcp::not_<Constraint> - negates a constraint.
  7. wcp::or_<Constraint1, Constraint2> - checks if at least one of constraints is not violated.
  8. wcp::and_<Constraint1, Constraint2> - checks if both constraints are not violated.

Event handling

And finally C++ developers may take advantage of multicast events and unicast delegates, just almost like .Net event handling:

     struct EventHandler
     {
         void OnEvent(const wcp::char_t* name)
         { std::tcout <<TEXT("Event: ") <<name <<std::endl; }
     };
  
     struct EventSource
     {
         typedef wcp::multicast_event<void, const wcp::char_t*> event_t;
         event_t SomeEvent;
     };
  
     EventHandler handler[2];
     EventSource source;
  
     // Declare delagates
     typedef EventSource::event_t::delegate_t delegate_t;
     delegate_t d1(handler[0], &EventHandler::OnEvent);
     delegate_t d2(handler[1], &EventHandler::OnEvent);

     // Subscribe delegates to an event
     source.SomeEvent += d1;
     source.SomeEvent += d2;

     // Fire event
     source.SomeEvent("Event Name");

     // Unsubscribe delegates to an event
     source.SomeEvent -= d1;
     source.SomeEvent -= d2;
Look at ConsoleCtrlHandler.cpp and ConsoleKbdHandler.cpp examples to see using of WCP delegates and events in practice.

And more other useful features...

Moreover, there are also a whole bunch of useful classes and macros in WCP: STL-compatible case insensitive string (std::stringi, std::wstringi, std::tstringi) and other STL extensions (wcp/stlex.h), string tools (wcp/strtools.h), extremely convenient thread local storage (wcp::tls), typed buffers (wcp::buffer) that allows you to operate with a buffer as with a vector of bytes, threads and threadpools (wcp::thread, wcp::thread_class, wcp::threadpool), runable tasks (wcp::task, wcp::safe_task), any object adaptor (wcp::any), reference adaptor (wcp::reference), Windows error handling (wcp::win_exception, wcp::win_verif), thread safe singleton with lazy initialization (wcp::safe_singleton), etc.

Just enjoy!

Last edited Jan 27, 2010 at 11:24 PM by astoyan, version 6