C%2b%2b To Java Converter

C (/ ˌ s iː ˌ p l ʌ s ˈ p l ʌ s /) is a general-purpose programming language created by Bjarne Stroustrup as an extension of the C programming language, or 'C with Classes'.The language has expanded significantly over time, and modern C now has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation. It is almost always. This service will translate the code for you, just start typing the code or upload a file to convert it. Supports converting code from VB.NET to C#, from C# to VB.NET, from C# to TypeScript and from VB.NET to TypeScript and Java to all others.

Douglas C. Schmidt

pjain@dre.vanderbilt.edu and schmidt@dre.vanderbilt.edu
Department of Computer Science
Washington University, St Louis

This work is supported in part by a grant from Siemens MedicalEngineering, Erlangen, Germany. The article appeared in the January1997 C++ Report magazine. Java has improvedquite a bit over the years and many of the problems identified in thisarticle have been fixed in later versions of Java.

1. Introduction

Over the past year, the Javaprogramming language has sparked considerable interest among softwaredevelopers. Its popularity stems from its flexibility, portability,and relative simplicity (compared with other object-orientedprogramming languages). Many organizations are currently evaluatinghow to integrate Java effectively into their software developmentprocesses.

This article presents our experiences applying the Java language andits run-time features to convert a large C++ communication softwareframework to Java. We describe the key benefits and limitations ofJava we found while converting our C++ framework to Java. Some ofJava's limitations arise from differences between language constructsin C++ and Java. Other limitations are due to weaknesses with Javaitself. The article explains various techniques we adopted toworkaround Java's limitations and to mask the differences between Javaand C++. If you're converting programs written in C++ to Java (ormore likely, if you're converting programmers trained in C++ to Java),you'll invariably face the same issues that we did. Addition insightson this topic are available from Taligent.

1.1. Background

Java is commonly associated with writing applets for World Wide Web(WWW) browsers like NetscapeNavigator and InternetExplorer. However, Java is more than a language for writing WWWapplets. For instance, Java's Abstract Window Toolkit (AWT) is a GUIframework that provides the core functionality for developingplatform-independent client applications. [AWT:96]. In addition, Java's native support formulti-threading and networking makes it attractive for buildingconcurrent and distributed server applications.

The Java team at Sun Microsystems intentionally designed the syntax ofJava to resemble C++. Their goal was to make it easy for C++developers to learn Java. Despite some minor differences (e.g., newkeywords for inheritance and RTTI), C++ programmers will be able to``parse' Java syntax easily. It took us about a day to feelcomfortable reading and writing simple Java examples.

Understanding the syntactic constructs of the language, however, isjust one aspect of learning Java. To write non-trivial programs, C++developers must understand Java's semantics (e.g.,copy-by-reference assignment of non-primitive types and type-safeexception handling), language features (e.g.,multi-threading, interfaces, and garbage collection), andstandard library components (e.g., the AWT andsockets). Moreover, to truly master Java requires a thoroughunderstanding of its idioms and patterns [Lea:96], many of which are different from thosefound in C++ [Coplien:92].

1.2. Converting from C++ to Java

We believe the best way to master Java (or any other programminglanguage) is to write substantial applications and reusablecomponents. Therefore, the material in this article is based on ourexperiences converting the ACE framework [Schmidt:94] from C++ toJava.ACE is a freely available object-oriented network programmingframework that makes considerable use of advanced C++ features (liketemplates and pointers-to-methods) and operating systems features(like multi-threading, shared memory, dynamic linking, andinterprocess communication). The following figure illustrates thearchitecture of the C++ version of ACE:
Figure 1. The C++ ACE Architecture

The ACE source code release contains over 85,000 lines of C++. ACEhas been ported to Win32, most versions of UNIX, VxWorks, and MVSOpenEdition. Approximately 9,000 lines of code (i.e., about 10% ofthe total toolkit) are devoted to the OS AdaptationLayer, which shields the higher layers of ACE from OSplatform dependencies.

As part of a project with the Siemens Medical Engineering group inErlangen, Germany, we've converted most of ACE from C++ to Java. Theprimary motivation for converting ACE to Java was to enhance Java'sbuilt-in support for networking and concurrent programmingfunctionality. Many ACE components (such as Task, Timer Queue, ThreadManager, and ServiceConfigurator), along with traditional concurrency primitives (suchas Semaphore,Mutex, and Barrier) wereconverted from C++ to the Java version of ACE. These ACE componentsprovide a rich set of constructs layered atop of Java's existinglibraries. In addition, the Java version of ACE includesimplementation of several distributedservices (such as a Time Service, Naming Service, and LoggingService).

The following figure illustrates the architecture of the Java versionof ACE:

Figure 2. The Java ACE Architecture

The Java version of ACE shown in Figure 2 is smaller than the C++version of ACE shown in Figure 1. There are several reasons for thereduction in size and scope:

Who is 2b
  • Portability -- The Java Virtual Machine (JVM)provides cross-platform portability. This eliminates the need for theOS Adaptation Layer that encapsulates the different C language OS APIsin the C++ version of ACE.
  • API Parsimony -- The Java run-time interface ismuch ``leaner' than UNIX and Win32 intefaces. For instance, the Javarun-time interface omits many of the interprocess communicationmechanisms (like Named Pipes, STREAM Pipes, TLI, and System V IPC) andsingle-threaded event demultiplexing mechanisms (likeselect, poll, andWaitForMultipleObjects) commonly found on UNIX and Win32.In addition, since Java doesn't support shared memory or memory-mappedfiles, the memory management wrappers (such as Mem_Mapand Shared_Malloc) were omitted from Java ACE.
In general, converting the ACE framework from C++ to Java wasrelatively straightforward. Most of our effort focused on (1) how tomap C++ features onto Java features and (2) how to modify the designof ACE accordingly. Our goal was to keep the ACE interface asconsistent as possible with the original C++ version of ACE.Fortunately, since the C++ version of ACE already existed, we used itto guide the decomposition of classes in the Java version of ACE.This illustrates the reuse of designpatterns and software architectures, many of which otherdevelopers can use when converting their C++ programs to Java.

1.3. Topics Covered in this Article

The following is a synopsis of the topics covered in this article:
  • The benefits of Java compared to C++ --

    Over the past decade, C++ has been our language of choice for buildinghigh-performance communication frameworks. A major strength of C++ isits efficient run-time performance and multi-paradigm support forobject-oriented programming and generic programming. However, afterboth teaching and applying C++ extensively in practice, we recognizethat it can be a complex language to learn and use. In ourexperience, a major source of C++'s complexity arises from explicitmemory management and lack of portability across compilers and OSplatforms. Not surprisingly, therefore, we found some of the mostimportant benefits of using Java were its garbage collection andstandard libraries. These features enabled us to develop portableJava applications rapidly without expending considerable efforttracking down memory management errors and wrestling with brokencompilers and incompatible development environments.

  • Limitations of Java compared to C++ (and workarounds) --

    While the syntax of Java resembles C++, certain features, semantics,and patterns are quite different. For instance, Java lacks manyfeatures commonly used in C++ programs. Chief among these aretemplates, pointers-to-methods, explicit parameter passing modes,enumerated types, and operator overloading.

    While the omission of these features simplifies the Java language, italso affects the way that developers fluent in C++ can design theirprograms. Converting C++ code to Java, while keeping the samefunctionality, can be tedious due to the absence of some C++ languageconstructs. In particular, we found that using Java oftensignificantly increased the number of classes that we wrote comparedto C++, where we would have employed templates to factor out commoncode.

    We recognize that omitting C++ constructs doesn't necessarily makeJava a less powerful language. In fact, we found that rethinking ouroriginal design of ACE using Java often solved the problem aseffectively as by using C++. Many of the limitations we describeoccurred because we were converting a large framework designed toexploit C++ features. Had we designed ACE as a Java framework fromscratch, some of these limitations wouldn't have been problematic.

    We expect that many other C++ developers are, or will soon be,programming in Java. We wrote this article to help capture anddocument how we used Java effectively for systems-level programming.Therefore, in addition to outlining Java's limitations, this articledescribes the techniques we used to workaround the lack of C++features like templates and pointers-to-methods.

This article focuses on the design and programming issues that arisewhen converting a large communication software framework written inC++ to Java and how to resolve these issues. Although the performanceof Java relative to C++ is a very important topic, it's beyond thescope of this article. We'll present performance measurements of theJava version of ACE compared to the C++ version of ACE in a subsequentarticle.

2. Key Benefits of Java

The Java programming language provides a rich set of language featuresand reusable components. The following are the key benefits of Javawe identified in converting ACE from C++ to Java:

2.1. Simplicity

One of the primary goals of the Java design team was to keep thelanguage simple and easy to learn. This was done by making it looksimilar to C++, but by omitting many C++ constructs. For example,several error-prone aspects of C++ (such as pointers and operatoroverloading) have been deliberately omitted from Java to make itsmaller, easier to learn, and easier to use. We found Java to be arelatively concise language that offers the benefits ofobject-oriented programming, plus a useful set of standard librarycomponents.

Learning Java and using it to convert ACE from C++ to Java was arelatively smooth process for us. It only took a few days to convertmajor portions of ACE from C++ to Java. Since many Java constructsare similar to C++, converting some of ACE to Java was mostly a matterof mapping the C++ constructs to their Java counterparts. Having donethis, it was relatively easy to build the higher-layer ACEcommunication services and example applications using Java.

For example, it took us less than half a day to reimplement the ACE distributed Time Service using the Java version of ACE. Ofcourse, one reason for our productivity was that we'd already figuredout how to design and implement the distributed Time Service in C++.However, the simplicity of Java also contributed significantly to thisrapid development cycle.

2.2. Portability

One of the most time consuming aspects of implementing the C++ versionof ACE has been porting it to dozens of OS platforms and C++compilers. This is a challenging task since not only must weencapsulate platform-specific system calls, but we must wrestle withmany broken C++ compilers (templates and exception handling areparticularly problematic in our experience).

In contrast, Java offers the promise of platform portability. This,of course, is due to the fact that Java is more than just a language-- it defines a Virtual Machine (the JVM) on which programsimplemented in the language will execute. Therefore, unlike C++(which generally treats platform issues outside the scope of thelanguage definition), the Java programming language can make certainassumptions about the environment in which it runs. This is one ofthe key factors that changes the idioms and patterns used by Javaprogrammers.

Java programs now run on many OS platforms (such as Solaris, WindowsNT, Windows '95, and OS/2) without requiring major modifications.However, as new Java development environments are released bydifferent vendors (who add their own bugs and extensions), it may behard to maintain such a high degree of portability. In addition,since Java doesn't support preprocessors or conditional compilationit's unclear how to encapsulate the inevitable platform differencesthat may arise in practice.

2.3. Built-in Threading and Networking Support

Java's built-in support for threading and networking was crucial toour conversion of ACE from C++ to Java. Although the Java environmentalso provides standard support for other common programming mechanisms(such as event-driven user-interfaces), we concentrate on Java'sthreading and socket mechanisms in this article since our focus is onconverting communication software frameworks from C++ toJava.

The java.net package and java.lang.Thread class provide useful classes for creatingconcurrent and distributed client/server applications. Using theseclasses simplifies client/server application development because theJava wrappers shield programmers from tedious and error-pronethreading and socket-level details. For example, the following codeshows how a simple ``thread-per-connection' server application can bewritten in Java: Note the relatively few lines of code required to write a simpleconcurrent server. Moreover, note how easily the server can bemulti-threaded by starting each ServiceHandler in itsown thread of control. Although the C++ version of ACE providesequivalent functionality and parsimony, it's harder to use featureslike exception handling, sockets, and threading portably acrossmultiple OS platforms.

The following code illustrates how a Java client application can bewritten to communicate with the server: The Java wrappers for sockets play a similar role as the C++ socketwrappers in ACE. They both provide a uniform interface thatsimplifies communication software implementations. In our conversionof ACE from C++ to Java, it was trivial to use the Java socketwrappers to provide a communication interface equivalent to the C++version of ACE.

2.4. Standard Libraries

The Java development environment provides several useful collectionsin the form of interfaces and classes. These are contained in thejava.util package and include Enumeration,Vector, Stack, Hashtable,BitSet, and Dictionary. Providing genericcollections as part of the standard development environment simpliesapplication programming. For example, in the C++ version of ACE,we've implemented reusable components (such as theMap_Manager and the Unbounded_Set) tosimplify the development of higher-level ACE components.

In contrast to Java, the standard components we used in the C++version of ACE were developed from scratch since they didn't exist asin all our C++ development environments. Although the ANSI/ISO draftstandard is nearing completion, most C++ compilers still don't providestandard libraries that are portable across platforms. Therefore,programmers must develop these libraries, port them from public domainlibraries (such as GNU libg++ and HP's STL), or purchase themseparately from vendors like RogueWave and Object Space.

For instance, to build a portable application in C++ that uses dynamicarrays, developers must either buy, borrow, implement, and/or port adynamic array class (such as the STL vector). In thecase of Java, developers can simply use the Vector classprovided in the development environment without concern forportability. The ubiquity of Java libraries is particularly importantfor WWW applets because the standard Java components can bepre-configured into browsers to reduce network traffic. In addition,the unified strategies provided by the Java standard libraries (suchas iteration, streaming,and externalization) provide idioms that Java programmers can easilyrecognize, utilize, and extend.

2.5. Garbage Collection and Range Checking

The Java run-time system performs garbage collection and rangechecking on pointer and array access. This eliminates a common sourceof errors in C and C++ programs. In Java, programmers don't need toexplicitly free memory resources acquired dynamically. Objects arefreed by the garbage collector when they are no longer referenced.This eases common programming traps and pitfalls associated withdangling references or memory leaks. [Editor's note: Robert Martin'sarticle in this issue on ``Java and C++: A Critical Comparison'discusses some drawbacks with Java's garbage collection model.]

There are tools (such as Purify, Bounds Checker, and Great Circle)that reduce the effort of writing memory-safe C++ code. However, it'sbeen our experience that even though these tools exist, C/C++programmers typically expend considerable effort avoiding memory leaksand other forms of memory corruption.

2.6. Type-safe Exceptions

Exceptions provide a clean way to handle error conditions withoutcluttering the ``normal' code path. Java provides an elegantexception handling model that includes both checkedexceptions and unchecked exceptions. Checked exceptionsallow the compiler to verify at compile-time that methods handlepotential exceptions. This allows Java to ensure that all checkedexceptions are handled. In addition, Java supports uncheckedexceptions, which allows developers to handle run-time exceptions anderrors.

We have consciously avoided the use of exceptions in the C++ versionof ACE due to portability problems with C++ compilers. In contrast,when converting the ACE framework from C++ to Java, we were able tomake extensive use of Java's exception handling mechanisms. Theability to use exception handling significantly improved the clarityof the Java ACE error-handling logic, relative to the C++ version ofACE.

Although Java exceptions are elegant, they also exact a performancepenalty. Depending upon how heavily exception handling is used, theimpact on performance can vary significantly. As mentioned earlier,performance measurements of the Java version of ACE will be covered ina subsequent article.

2.7. Loading Classes Dynamically

The Java run-time system loads classes ``on-demand' as they areneeded. Moreover, Java can load classes both from the file system, aswell as over the network, which is a powerful feature. In addition,Java provides programmers with the hooks to load classes explicitlyvia the java.lang.ClassLoader mechanism.

The java.lang.ClassLoader is an abstract class thatdefines the necessary hooks for Java to load classes over the networkor from other sources such as the file system. This class made iteasy to implement the ServiceConfigurator framework in ACE. The ACE Service Configuratorframework provides a flexible architecture that simplifies thedevelopment, configuration, and reconfiguration of communicationservices. It helps to decouple the behavior of these communicationservices from the point in time at which these services are configuredinto an application.

Implementing the Service Configurator framework in the C++ version ofACE is challenging because C++ doesn't define a standard mechanism fordynamic linking/loading of classes and objects. Implementing thisfunctionality across platforms, therefore, requires variousnon-portable mechanisms (such as OS support for explicit dynamiclinking). Moreover, since the draft ISO/ANSI C++ standard doesn'taddress dynamic linking, C++ compilers and run-time systems are notrequired to support dynamic linking. For instance, many operatingsystems will not call the constructors of static C++ objects linked indynamically from shared libraries.

The fact that Java provides standard mechanisms for dynamiclinking/loading of classes significantly simplified the implementationof the ACE Service Configurator framework. This reiterates the factthat Java is more than just a programming language. It defines arun-time environment, and can therefore make certain assumptions aboutthe environment in which it runs. Thus, unlike other languages suchas C and C++, the Java run-time environment can portably supportimportant run-time features such as explicit dynamiclinking/loading.

2.8. Using JavaDoc for Documentation

The JavaDoc tool generates API documentation in HTMLformat for the specified package or for individual Java source filesspecified on the command line. Using JavaDoc is straightforward. Thesyntax /** documentation */ indicates a documentationcomment (a.k.a., a ``doc comment') and is used by theJavaDoc tool to automatically generate HTMLdocumentation. In addition, doc comments may contain special tagsthat begin with the @ character. These tags are used byJavaDoc for additional formatting when generating documentation. Forexample, the tag @param can be used to specify aparameter of a method. JavaDoc extracts all such entries containingthe @param tag specified inside a doc comment of a methodand generates HTML documentation specifying the complete parameterlist of that method.

The C++ version of ACE also provides automatic generation ofdocumentation using a modified version of the freely available OSEtools. The ACE documentation tools produce UNIX-style man pages (innroff format), as well as JavaDoc-style documentation (in HTMLformat).

3. Key Limitations of Java and Our Workarounds

This section describes key limitations with Java we identified whenconverting ACE from C++ to Java. We've ordered our discussion in theorder of the impact that each limitation had on the conversion of ACEfrom C++ to Java.

Note that some of the limitations arose because we were converting anexisting C++ framework to Java. Had we originally developed ACE fromscratch using Java, many problems we encountered, and which wedescribe here as the ``limitations of Java,' would have beennon-issues. However, we suspect that when C++ programmers learn Java,or first convert code written in C++ to Java, they are likely to facethe same issues that we did. Therefore, in addition to outlining thelimitations with Java, this section describes the workarounds weapplied to alleviate the limitations.

3.1. Lack of Templates

The version of Java available to us (version 1.0.2) does not supporttemplates. This was a significant problem when converting ACE fromC++ to Java since ACE uses templates extensively. Templates are usedin ACE for two purposes:
  1. Common code factoring -- A typical use of templates in ACE is to avoid tedious recoding of algorithms and data structures that differ only by their types. For instance, templates factor out common code for programming abstractionssuch as the ACE_Map_Managerand ACE_Mallocclasses.

    One workaround for Java's lack of templates is to use Object-based containers like the Smalltalk-style collections available from Doug Lea or the Java Generic Library (which is a conversion of the Standard Template Library from C++ to Java) available from Object Space. [Editors note: Graham Glass' STL in Action column in this issue describes the design of the Java Generic Library.]

    These solutions are not entirely ideal, however, since they require application programmers to insert casts into their code. Although Java casts are strongly-typed (which eliminates a common source of errors in C and C++ programs that use traditional untyped casts), it is hard to optimize away the overhead of run-time type checking.

  2. Signature-based type conformance -- Another use of templates in ACE is to implement signature-based type conformance. For instance, the ACE_Acceptor and ACE_Connector are parameterized with a class that must conform to the ACE_Svc_Handler interface. An ACE_Svc_Handler is created and initialized when a connection is established either actively or passively. It is an abstract class that applications can subclass to provide a concrete service handler implementation. To parameterize the ACE_Acceptor with an ACE_Svc_Handler, (e.g., HTTP_Svc_Handler), we would do the following in C++ ACE:The C++ code parameterizes the ACE_Acceptor staticallywith the HTTP_Svc_Handler. The advantage of usingtemplates is that there's no run-time function call overhead. Anotheradvantage is the ability to parameterize locking mechanisms. Forinstance, the C++ version of ACE uses templates to select anappropriate synchronization strategy (e.g., mutexes vs. readers/writerlocks), as well as to remove all locking overhead when there is noconcurrency.

    Although Java lacks templates, it contain some interesting featuresthat enabled us to support signature-based type conformance by using apattern based on its meta-class facilities. For instance, here's howthe Java ACE Acceptor is defined: To achieve the C++ ACE behavior in Java ACE, a Class object can becreated using the class name ``HTTPSvcHandler' and this can then bepassed to the constructor of Acceptor, as follows: Once the acceptor object is initialized to listen on awell-known port, the accept method will acceptconnections and create HTTPSvcHandler objects tocommunicate with clients.

    The Java code uses the Class object created using thestring corresponding to the name of the SvcHandlerfactory as a parameter to the constructor of Acceptor.The Java ACE Acceptor uses this to create a new instanceof the SvcHandler when needed. As long asHTTPSvcHandler is a subclass of SvcHandler,a new Class object can be created and passed to theAcceptor. Therefore, the signature will match that expected by theAcceptor factory.

3.2. Lack of Enumerated Types

There are three common uses for enumerated types in C++: valueabstraction, type safety, and sequentialdeclarations (for use with arrays and switch statements). In C++,enumerated types are strongly typed. That is, unless you use anexplicit cast, the C++ compiler won't allow you to assign instances ofdifferent enumerated types to each other, nor can you assign integralvalues to instances of an enumerated type. In addition, enumeratedtypes convert automatically to their integral values, which iscommonly used for efficient table-based dispatching ofpointers-to-methods.

In Java, there are no enumerated types. This typically isn't aproblem when developing new code, or when writing in an orthodox``object-oriented' style, because subclasses and the JavainstanceOf typesafe dynamic cast feature can be used inplace of enumerals (as illustrated below).However, there were several situations where lack of enumerated typeswas a problem when converting ACE from C++ to Java:

  1. Lack of abstraction and type-safety -- Since Java doesn'tprovide enumerated types, programmers need to use primitive types(such as int) instead. For example, translating thefollowing C++ ACE code:into Java code is tedious and error-prone. The result looks like this:Not only is this less concise, but it is also more error-prone becauseany value of type int can be accidentally passed to theNamingMsgBlock constructor. Moreover, enumeral valuescan be duplicated accidentally. In contrast, the C++ type-systemensures that only NamingMsgType parameters are passedas arguments to the constructor ofACE_NamingMsgBlock.

    One workaround in Java for the lack of enumerated types is to usesubclassing. In this approach, a base class calledNamingMsgType is defined and a subclass ofNamingMsgType is created for each type ofNamingMsgType. The following code illustrates thiscommon Java pattern:Now we can ensure that an argument of typeNamingMsgType is passed to the constructor ofNamingMsgBlock. Here's how we can do a ``switch' todetermine the type of the message:

  2. Lack of efficient dispatching -- Although the Javasubclassing pattern solves the problem of type-safety, Java's lack ofenumerated types precludes the common C/C++ pattern of using enums asindices into arrays for efficient method dispatching. For instance,the C++ ACE_Name_Handlerimplementation dispatches the appropriate method by using the messagetype to index into a table of pointers to C++ methods. The followingcode demonstrates how a table of pointers to methods can be created todispatch efficiently based on enum Naming_Msg_Typeliterals (to simplify the example, some ACE C++ class names have beenchanged): Our dispatch routine is straightforward:Building this type of efficient dispatch mechanism in Java requiresthe use of a primitive type like int for the messagetype, which incurs the drawbacks described above. A workaround thisproblem is presented in Section 3.3, alongwith a workaround for Java's lack of pointers to methods.

3.3. Lack of Pointers to Methods

As the preceding example demonstrated, the lack of enums in Javaprecluded us from doing efficient dispatching without using primitiveintegral types. Not only does Java lack enums, however, it also lackspointers to methods. Pointers to methods are widely used in GUIframeworks and other event-based systems that demultiplex individualmethods based on the arrival of messages.

A common workaround for Java's lack of pointers to methods is to buildcallback objects via subclassing [Lea:96]. Thiscan be tedious, however, as shown by the following Java rewrite of theC++ ACE_Name_Handler class presented earlier. Theexample below also illustrates another solution to Java's lack ofenumerated types:

Here is how we can define the NameHandler class. Thisexample first uses the class NamingMsgType to emulate thefunctionality of enums, as well as to provide abstraction andtype-safety.The following NameHandler class uses aVector to keep instances of callback objects and uses thedispatch routine to extract the right instance of thecallback object:

Converter

Note that if the values of the NamingMsgType``enumeration' weren't contiguous, we could use the JavaHashTable rather than a Vector.

3.4. Lack of ``Conventional' Concurrency Mechanisms

Java is a multi-threaded language with built-in language support forthreads. The java.lang.Thread class contains methods forcreating, controlling, and synchronizing Java threads. The mainsynchronization mechanism in Java is based on Dijkstra-styleMonitors. Monitors are a relatively simple and expressivemodel that allow threads to (1) implicitly serialize their executionat method-call interfaces and (2) to coordinate their activities viaexplicit wait, notify, andnotifyAll operations.

While Java's simplicity is often beneficial, we encountered subtletraps and pitfalls with its concurrency model [Cargill:96]. In particular, Java'sMonitor-based concurrency model can be non-intuitive and error-pronefor programmers accustomed to developing applications using threadingmodels (such as POSIX Pthreads, Solaris threads, or Win32 threads)supported by modern operating systems. These threading models providea lower-level, yet often more flexible and efficient, set ofconcurrency primitives such as mutexes, semaphores, conditionvariables, readers/writer locks, and barriers.

In general, Java presents a different paradigm for multi-threadedprogramming. We found that this paradigm was initially non-intuitivesince we were accustomed to conventional lower-level multi-threadedprogramming mechanisms such as mutexes, conditionvariables, and semaphores. As a result, we had to rethink many ofthe concurrency control and optimization patterns used in C++ ACE. Wefound that converting C++ ACE code that used these conventionalsynchronization mechanisms required careful analysis and often changedthe implementation of C++ ACE components. This was due to differencesin Java concurrency mechanisms, compared with conventional POSIXPthreads-like threading mechanisms used in C++ ACE. The followingdiscussion explores some threading challenges we faced when convertingC++ ACE to Java:

  • Ambiguous timeout semantics in the wait method --To implement Monitors, Java defines the wait,notify, and notifyAll methods in classObject. Thus, all classes implicitly inheritthese methods. The wait method allows a thread to waitfor a condition to occur on an object, whereas the notifyand notifyAll methods allow a thread to signal otherwaiting threads when the condition associated with an object becomestrue.

    There are three forms of wait: Using the first form of wait, a thread performs ablocking wait on an object until it is notified by anotherthread. Using the other two forms of wait, an objectblocks until it is notified or the timeout expires. Timeouts are veryuseful when writing robust protocols that won't block indefinitely ifpeers misbehave.

    Unfortunately, Java doesn't directly inform applications whether thewait call returned because the object was notified orbecause a timeout occurred. This differs from the C++ version of ACE,which uses the condition variable mechanism in the underlying OSthreading packages. In C++ ACE, the ACE_Condition::waitmethod returns a value that explicitly disambiguates between timeoutsand notifications.

    Java's lack of an explicit return value or exception to differentiatebetween notification and timeout increases the responsibilities ofapplication programmers. For example, when converting the thread-safeACE_Message_Queueto Java, we first tried to implement the method enqueueas follows:The solution above won't work in Java since the Java waitmethod doesn't distinguish between returns due to timeoutsvs. notifications. In particular, the wait call mightreturn because of a notifyAll or because it wasinterrupted, rather than because it timed out.

    Our first solution to the ambiguity inherent in Java's timedwait used an algorithm presented by Doug Lea in [Lea:96]. This algorithm involves explicitlydetermining if the timeout has occurred, as follows:Although this algorithm solved the problem in this case, we wanted togeneralize the solution. Our goal was to avoid replicating commoncode in enqueue and dequeue and to create areusable TimedWait class utility. However, this turnedout to require very careful thought. The problem is that theifFull condition logic must be factored out of theenqueue method.

    As usual, it's ``patterns to the rescue.' We'll start by using theTemplate Method pattern [GoF:95] to implement ageneric TimedWait abstraction:We use the Template Method pattern to (1) define the skeleton of thealgorithm that computes the timeout in the timedWaitmethod and (2) defer the definition of the condition hookmethod to subclasses. Thus, subclasses can redefine thecondition logic without changing thetimedWait algorithm.

    Although our TimedWait class works for simple usecases,we can't directly extend MessageQueue fromTimedWait since TimedWait only allows us tospecify one condition at a time (i.e., it only has a singlecondition hook). This is overly restrictive for theMessageQueue implementation since itsenqueue and dequeue methods depend upon twoconditions: !isFull() and !isEmpty(),respectively.

    Once again, it's patterns to the rescue. In this case, we cangeneralize the TimedWait solution by applying anotherpattern -- a variant of the Delegated Notification pattern from [Lea:96] that we call BorrowedMonitor. By using the Borrowed Monitor pattern, thecondition hook of the TimedWait classdelegates to the appropriate implementation of theMessageQueue's isFull orisEmpty methods, as follows:Finally, we can put all the patterns and classes together to create aMessageQueue that uses the timedNot{Full|Empty}Condition classes defined above: This solution allows us to perform the timeouts withoutreimplementing the low-level time-out logic in theenqueue and dequeue methods.

    It's important to note that our TimedWait scheme onlyprovides approximate timeout granularity. In particular, the timeoutis really a lower bound since the enqueue anddequeue methods may not return immediately when a timeoutoccurs if they encounter contention when trying to reacquire themonitor lock. Moreover, in theory, the scheme doesn't even guaranteeliveness because a thread may block indefinitely waiting to reclaimthe monitor lock. This is extremely unlikely to be a problemin practice, however.

    It is worthwhile to point out that all these classes and patternswould be unnecessary if Java simply differentiated between timeoutsand ``normal' notifications in the first place! Unfortunately, thecurrent semantics of Java's wait(long timeout) methodmakes this impossible.

  • Error-prone nested monitor semantics --If you look closely at the definition of TimedWaittimedWait method, you'll notice that it is notadorned with the synchronized keyword. This omission isnecessary to avoid ``nested monitor lockout,' which is surprisinglycommon in Java.

    The nested monitor problem occurs when a thread acquires objectX's monitor lock, e.g., TimedWait, withoutrelinquishing the lock already held on monitor Y, e.g.,MessageQueue, thereby preventing a second thread fromacquiring the monitor lock for Y. This can lead to a lockoutoccurring since after acquiring monitor X, the first thread maywait for a condition to become true that can only change as a resultof actions by the second thread after it has acquired monitorY. Naturally, this can't happen as long as the firstthread holds X's monitor lock...

    The following example is based on an example in [Lea:96] and illustrates the nested monitor lockoutproblem: The code above illustrates the canonical form of the the nestedmonitor problem in Java. When a Java thread blocks in the monitor'swait queue, all its locks are held except the lock of theobject placed in the queue [Lea:96]. Considerwhat would happen if a thread T made a call toOuter.process and as a result blocked in thewait call in Inner.awaitCondition. SinceInner and Outer classes don't share theirmonitor locks, the awaitCondition call would release theInner monitor, while retaining the Outermonitor. However, no other thread can acquire the Outermonitor since it's locked by the synchronized processmethod. Therefore, no thread can call Outer.set to setthe condition to be true. As a result, T would continue to beblocked in wait forever.

    There are several ways to avoid the nested monitor problems in Java [Lea:96]. In our particular example ofMessageQueue, the solution to the nested monitor problemis to not declare TimedWait.timedWait as asynchronized method. Instead, we borrow the monitor lock(which must already be held) from TimedWait.object_.Thus, in the following code:the notFullCondition_.timedWait call will end up callingwait on the MessageQueue instance (which isstored within TimedWait in the object_field). Therefore, the wait operation will automaticallyrelease the MessageQueue's monitor lock. This is thecorrect behavior since it avoids nestedmonitor lockout.

    Although this solution works, it is non-intuitive and overly subtleunless you are intimately familiar with both Java's Monitor semanticsand patterns like Template Method, Delegated Notification, andBorrowed Monitor. In our experience, detecting and fixing nestedmonitor lockout in Java is tricky. In fact, this example illustrateshow the simplicity of Java's threading semantics can be a limitation.In general, we found that while implementing simple concurrency modelsin Java is easy, implementing more complex concurrency models oftenrequires heroic efforts, particularly when trying to alleviatedeadlock, race conditions, and synchronization overhead. Therefore,it's crucial to understand Java's threading semantics and designpatterns thoroughly to avoid these kinds of nested monitor problems [Lea:96].

  • Non-portable scheduling semantics --The Java Virtual Machine (JVM) makes it easier for programmers torapidly develop portable applications. In particular, it eliminatesthe need to expend considerable effort porting middleware andapplications among various OS platforms and language developmentenvironments. However, writing portable mulit-threaded Javaapplications remains problematic for the following reasons:
    • Non-standard scheduling semantics -- The Java specification doesn't dictate the type of run-time threadscheduler implementation (i.e., preemptive vs. non-preemptive).In addition, it doesn't require the scheduler to be ``fair.'Therefore, an unlucky thread can starve forever while waiting toacquire a resource if (1) the thread scheduler is non-preemptiveand/or (2) there is sufficient ongoing competition from other threads.One workaround for this problem is to insert Thread.yieldcalls into the code to give other threads a chance to run. However,this solution can be tedious, error-prone, and inefficient since itrequires the programmer to second-guess the level of concurrency inthe application and the Java run-time system.
    • Haphazard notification -- There is no way tospecify a particular thread or a group of threads that should benotified. Several Java books incorrectly state thatnotify wakes up the thread that has been waiting thelongest. Our solution in Java ACE was to port the ACE_Tokenclass, which implements the Specific Notification pattern[Cargill:96] in order to wake up waitingthreads in a deterministic order.
    • Priority inversion -- Thread priority has nobearing on Java's notification semantics. For example, a higherpriority thread is not favored over a lower priority one whenthe scheduler selects which thread blocked in a wait shouldbe awakened by a notify.
    Some of the problems described above exist in part because thesimplicity of Java's concurrency model makes it hard for applicationsto take advantage of powerful features available in advanced OSplatforms. For instance, Solaris provides system calls that allowapplications to control the scheduling behavior of OS threads (whichis important for real-time multi-media, avionics, and process-controlapplications). However, Java doesn't export this functionality toapplications since it isn't portable to other OS platforms.
  • Lack of encapsulation for synchronization -- Java'sconcurrency control mechanisms expose the synchronizedkeyword in the public interface of a class. Unfortunately, this canbreak object encapsulation since it's possible for clients to disruptthe locking protocol of any Java object with synchronized methods.For instance, a client can accidentally lock aMessageQueue, as follows: In C++, this type of problem can be prevented by not exposing thelocking mechanism to clients, as follows: Naturally, the C++ solution is not impervious to malicious programmersor to other classic sources of program corruption in C/C++ (such asscribbling over memory due to stray pointers).

3.5. Lack of Destructors

C++ destructors are commonly used to manage reusable resources (suchas memory, I/O descriptors, locks, window buffers, etc.) that must bereleased before leaving a scope. This leads to common patterns in C++where constructors and destructors collaborate to ensure thatresources are acquired on entry, and released on exit, from a block ofcode.

For instance, consider the following simplified version of the C++ ACE_Message_Queue(for simplicity, most error handling code has been omitted):

Since the destructor is called automatically once the class goes outof scope, we can ensure that the contents in the queue are released.This ``acquire/release' protocol is particularly important if thecontents of the queue are resources (like locks or sockets) that arerelatively scarce, compared with virtual memory.

Java lacks general-purpose destructors. Instead, it providesconstructs like finally and finalize tomanage the release of resources. The ACE_Message_Queueclass shown above can be written in Java using the constructfinalize as follows: Similarly, the Java finally construct can be used insidea method to ensure that resources are released when the method goesout of scope (even if an exception is thrown). Here's an example fromACE in which the svc method uses aMessageQueue to process several messages. To ensure thatthe MessageQueue is properly closed (and the resourcesreleased) when the method goes out of scope, we use the Javafinally construct. Unfortunately, Java's finally and finalizeconstructs exhibit the following two problems:

  1. Manual intervention -- In the case offinally in Java ACE, the cleanup code needs to beinserted manually in finally blocks, which can be tediousand error-prone.In contrast, C++ ACE makes it easier since the compiler ensures that adestructor of the MessageQueue class is calledautomatically on exit from the svc method's scope.Therefore, programmers need not insert finally blockscontaining cleanup code into multiple blocks.
  2. Premature resource exhaustion -- In the case offinalize, the problem centers around ``lazy deletion.'Since the garbage collector may not get run until the application runslow on memory, this approach is prone to premature resourceexhaustion. In particular, a program that queues many locks or socketdescriptors will run out of these resources long before running out ofmemory. Java does provide hooks to allow programmers to explicitlyforce garbage collection using the Runtime class'sgc method. In addition, the Runtime classalso provides a runFinalization method that runs anypending class finalizers when invoked. However, doing any of thisrequires manual intervention on the part of the programmers. Sincegarbage collection takes time, running it explicitly multiple timesmay not be desirable in time-critical applications.

3.6. Lack of Explicit Parameters Passing Modes

In contrast to other OO languages like C++ and Ada95, Java doesn'tsupport user-specified parameter passing modes. Instead, Java hasreference semantics for object paramters, but passes these referencesby value. For example, in the following code, even after thesomeMethod invocation, bar still points tothe String object associated with the literal string``original.'We encountered this problem in ACE when we tried to implement theclass ACE_SOCK_Stream, which encapsulates the OS datatransfer mechanisms for sockets. The recv andrecv_n methods of C++ ACE rely on the message beingpassed back to the caller by reference. Since Java only passesreferences ``by value,' this became problematic.

To circumvent this limitation when converting ACE from C++ to Java, weidentified the following four solutions:

  1. Add an extra level of indirection -- We appliedthis common pattern and added Java wrappers around classes we wantedto pass by reference. Then, we passed instances of the wrapper class.Thus, in the example above, we created a wrapper string class calledCString. We then passed a reference to this wrapperobject that contained the actual String. Here's what theCString class looks like: With this change, the String object can now be changedboth by the caller and the callee. Thus, the firstsomeMethod example can now be rewritten as follows:
  2. Use one-element array -- The second solution passedthe String object in a one-element array. Using this solution, thefirst someMethod example can be rewritten as follows: Although this syntax is somewhat ugly, it is relatively concise.Moreover, we can generalize this approach to pass in multiple objectsby reference via a multiple-element array.
  3. Use mutator operators -- Our third solution usedmutator methods. For instance, in our example above, wecould use StringBuffer instead of String topass the messages between the caller and the callee. Using thissolution, the above example can be rewritten as follows:Note that this is the solution we used in implementingSOCKStream in ACE Java. This is because, this solutionrequired the least amount of change to our original design.Unfortunately, the numeric wrappers (e.g., class Doubleand Integer) lack mutator operations. Therefore, theyaren't useful for returning scalar parameters by reference (despitethe claims made by certain Java books...). This forces programmers towrite many additional (and incompatible) wrapper classes that allowthese types to be passed by reference.
  4. Return objects by value -- The fourth solutionrelies on the callee returning a modified version of the object as thereturn value to the caller. The caller then assigns the return valueto the original object in order to ``update' it. Using this solution,the above example can be rewritten as follows:However, to generalize this approach to return multiple values byreference requires defining even more helper classes.

3.7. Lack of Explicit Inline Functions

The lowest-level of ACE is the OS adaptation layer. This layerconsists of C++ OSwrappers that shield systems programmers from using the tedious,error-prone, and non-portable native OS APIs written in C. In C++,there is essentially no cost for adding this extra level ofabstraction since inlinestatic functionscan be used.

In contrast, adding an extra level of abstraction can increase thecost of method calls in Java since it does not support explicitinlining. There are various patterns for handling this problem usingthe Sun JDK.For instance, the JDK Java compiler does allow the user to specify'-O' flag, which does some optimization, but the user has no directcontrol over this without using convoluted, non-portable techniques.Whenever possible, methods (or entire classes) in Java should bedeclared final to facilitate inlining.

In general, Java doesn't provide standard hooks (like the C/C++register keyword) to programmers to manually suggestoptimizations. Instead, Java relies on effective compileroptimizations that, in theory, remove the need for programmers to``hand-optimize' performance. In practice, it remains to be seenwhether Java compilers can perform sufficient optimizations to competewith the performance of C/C++ compilers.

4. Concluding Remarks

This article presents our experience converting the ACE communicationsoftware framework from C++ to Java. Overall, the conversion processhas been both exciting and frustrating. It was exciting because wehad the opportunity to explore Java features that aren't available inC++ (such as meta-class programming), as well as to validatecommunication software designpatterns we'd originally discovered using C++. In addition,converting ACE to Java provided us with many insights on the benefitsand limitations of using Java for industrial-strength frameworkdevelopment, particularly for concurrent network servers.

The following are our recommendations to developers who plan to buildsystems in Java, either from scratch or by converting code originallywritten in C++:

  • Be prepared for a paradigm shift -- Although Java syntaxlooks like C++, many of its semantics, features, and patterns aredifferent from C++. In addition, the Java run-time system differs inkey respects from conventional OS platforms like UNIX and Win32. Inparticular, Java's threading model is different than the model used bySolaris threads, POSIX Pthreads, and Win32 threads. This is not acriticism of Java, per-se, but it demonstrates that developers with abackground in other languages and operating systems should be preparedfor the ``paradigm shift' required to use Java effectively.
  • Recognize and exploit Java's strengths -- An importantlesson from our conversion effort was how certain Java featuressimplify the development of communication software. In particular,built-in support for sockets and threads, automatic memory management,and exception handling made it possible to convert many C++ ACEcomponents to Java with relative easy. Depending on the context, theeffort required to develop applications using Java can be less thandeveloping the application using C++. This is particularly true ifthe application uses dynamic memory heavily and/or must run portablyacross multiple OS platforms.
  • Pay close attention to code that uses C++-specific languagefeatures -- Several aspects of our conversion were frustratingbecause translating certain ACE components from C++ to Java wasnon-trivial. Many ACE components use C++-specific language features(such as templates and pointers-to-methods) and programming patterns(such as ``constructors acquire resources, destructors releaseresources'). Translating these components to Java directly wastedious due to the lack of certain language features. For instance,lack of templates and lack of enum abstraction, coupled with thelimitation of passing references by value, increased our programmingeffort compared with the existing C++ solutions.
  • Apply workarounds when needed -- We found it was oftentricky to convert C++ code directly to Java while keeping theimplementation as close as possible to the original C++ design.By understanding the differences between C++ and Java, however, wewere able to employ various workarounds to successfully convert theACE framework from C++ to Java. When converting C++-based designs orcode written in C++ to Java, you'll invariably face the same hurdlesthat we did. In some cases, you'll need to rethink your design in``Java terms' to overcome these hurdles. To ease the transition, youmight find it useful to apply the workarounds we presented in thisarticle.
As more systems are developed using Java, the strengths and weaknessesof Java will become more apparent. Whether these systems aredeveloped from scratch using Java or are being converted from C++,time will tell how well Java suits the needs of the industry. Ourgoal in writing this article was to help other C++ developers benefitfrom our experiences in order to use Java effectively in theirprojects.

The C++ and Java versions of ACE are freely available via the WWW atURL ACE.html.

5. Acknowledgements

We would like to thankChris Cleeland, David Holmes,Doug Lea, David Levine, and Greg Wilson for comments on earlier drafts of this article.

Conversion Of C Program To Java

6. References

[Lea:96]Doug Lea, Concurrent Java: DesignPrinciples and Patterns, Addison-Wesley, 1996.

[AWT:96]AWTComponents, Source: http://java.sun.com/tutorial/ui/overview/components.html

Actually, Having A Quick Look At The Source Code No Automatic Translator Will Do A Good Job On The Conversion. The Code Is Based On Too Many Librar...

[Coplien:92]James Coplien.Advanced C++ Programming Styles and Idioms. Reading, MA.:Addison-Wesley, 1992.

[Schmidt:94]Douglas C. Schmidt, ACE: anObject-Oriented Framework for Developing Distributed Applications,Proceedings of the 6th USENIX C++ Technical Conference, Cambridge,Massachusetts, April, 1994.

[Cargill:96]SpecificNotification for Java Thread Synchronization, Proceedings of the3rd Pattern Languages of ProgrammingConference, Allerton Park, Illinois, September, 1996.

C 2b 2b To Java Converter Free

[GoF:95] E. Gamma, R. Helm,R. Johnson, and J. Vlissides, DesignPatterns: Elements of Reusable Object-Oriented Software,Reading, MA: Addison-Wesley, 1995.

This page is maintained by Prashant Jain and Douglas C. Schmidt. If youhave any comments or suggestion for improving this document, pleasesend us email.

Decimal To Binary Conversion Program In C/c /java

Last modified 18:06:18 CST 25 January 2019