Method that only one other class type may access

Discussion in 'C++' started by Christopher, Oct 1, 2011.

  1. Christopher

    Christopher Guest

    I know I need some sort of friend, but I don't want to friend the
    entire class.

    I've got a TcpConnection class and it has a state member.
    I've got a TcpListener class that accepts connections and creates a
    TcpConnectionClass.

    The TcpListener class needs to be able to change the state member of
    the TcpConnection class, but I don't want anyone else to be able to.
    How do I go about that?
    Christopher, Oct 1, 2011
    #1
    1. Advertising

  2. Christopher

    Christopher Guest

    On Oct 1, 3:32 pm, Christopher <> wrote:
    > I know I need some sort of friend, but I don't want to friend the
    > entire class.
    >
    > I've got a TcpConnection class and it has a state member.
    > I've got a TcpListener class that accepts connections and creates a
    > TcpConnectionClass.
    >
    > The TcpListener class needs to be able to change the state member of
    > the TcpConnection class, but I don't want anyone else to be able to.
    > How do I go about that?


    Problem 1 is that the first class needs to be aware of the second, so
    when I use friend function I have a circular dependency problem when
    keeping seperate headers.

    Problem 2 is that the function I want to friend is virtual. I cannot
    find whether or not making it a friend also makes its derived type
    implementations of that function a friend also.

    Problem 3 is that I will also derive from the class I am allowing
    access to.
    So, I want class B and all its derived types to have access
    to the data in class A and all its derived types.
    Christopher, Oct 1, 2011
    #2
    1. Advertising

  3. On 01.10.2011 22:32, Christopher wrote:
    > I know I need some sort of friend, but I don't want to friend the
    > entire class.
    >
    > I've got a TcpConnection class and it has a state member.
    > I've got a TcpListener class that accepts connections and creates a
    > TcpConnectionClass.
    >
    > The TcpListener class needs to be able to change the state member of
    > the TcpConnection class, but I don't want anyone else to be able to.
    > How do I go about that?


    Most likely you have a design problem. A listener should not change any
    other state than its own. A listener is a listener and a connection is a
    connection. Why should a listener manipulate the connection state. What
    about race conditions?

    There are ways to do so, but I am in doubt whether this is a good
    advise. You could create a base class of your connection to isolate the
    state member and make the listener a friend of this base class.


    Marcel
    Marcel Müller, Oct 1, 2011
    #3
  4. Christopher

    Christopher Guest

    On Oct 1, 4:38 pm, Marcel Müller <> wrote:
    > On 01.10.2011 22:32, Christopher wrote:
    >
    > > I know I need some sort of friend, but I don't want to friend the
    > > entire class.

    >
    > > I've got a TcpConnection class and it has a state member.
    > > I've got a TcpListener class that accepts connections and creates a
    > > TcpConnectionClass.

    >
    > > The TcpListener class needs to be able to change the state member of
    > > the TcpConnection class, but I don't want anyone else to be able to.
    > > How do I go about that?

    >
    > Most likely you have a design problem. A listener should not change any
    > other state than its own. A listener is a listener and a connection is a
    > connection. Why should a listener manipulate the connection state. What
    > about race conditions?
    >
    > There are ways to do so, but I am in doubt whether this is a good
    > advise. You could create a base class of your connection to isolate the
    > state member and make the listener a friend of this base class.
    >
    > Marcel


    Well, the listener accepts connections right?
    So, it creates a connection in the NOT_CONNECTED state.
    It queues it up for an accept via boost::asio
    When the accept completes the listener is notified via boost::asio
    the listener has to be notified, so it can add the connection to its
    collection of clients
    Now we have a connected connection in a NOT_CONNECTED state = bad.

    I was fiddling with the boost::binds that the boost::asio example uses
    and was thinking maybe:
    I could have the listener post the accept request with a callback to
    the connection itself
    Have the connection itself, in its callback, call a callback on the
    listener.

    I don't want to turn things in to spaghetti though.
    Christopher, Oct 1, 2011
    #4
  5. On 02.10.2011 00:34, Christopher wrote:
    >> Most likely you have a design problem. A listener should not change any
    >> other state than its own. A listener is a listener and a connection is a
    >> connection. Why should a listener manipulate the connection state. What
    >> about race conditions?
    >>
    >> There are ways to do so, but I am in doubt whether this is a good
    >> advise. You could create a base class of your connection to isolate the
    >> state member and make the listener a friend of this base class.

    >
    > Well, the listener accepts connections right?
    > So, it creates a connection in the NOT_CONNECTED state.
    > It queues it up for an accept via boost::asio
    > When the accept completes the listener is notified via boost::asio
    > the listener has to be notified, so it can add the connection to its
    > collection of clients
    > Now we have a connected connection in a NOT_CONNECTED state = bad.


    Move the connection pool to the connection class (or a separate
    connection pool class) and provide a public factory function that
    initiates a connected connection.


    > I was fiddling with the boost::binds that the boost::asio example uses
    > and was thinking maybe:
    > I could have the listener post the accept request with a callback to
    > the connection itself
    > Have the connection itself, in its callback, call a callback on the
    > listener.


    Sorry, I didn't catch what you mean.


    Marcel
    Marcel Müller, Oct 2, 2011
    #5
  6. Christopher

    Noah Roberts Guest

    On Oct 1, 1:32 pm, Christopher <> wrote:
    > I know I need some sort of friend, but I don't want to friend the
    > entire class.
    >
    > I've got a TcpConnection class and it has a state member.
    > I've got a TcpListener class that accepts connections and creates a
    > TcpConnectionClass.
    >
    > The TcpListener class needs to be able to change the state member of
    > the TcpConnection class, but I don't want anyone else to be able to.
    > How do I go about that?


    I think you could do this with a function object. Something like this
    untested bit:

    struct TcpListener;

    struct TcpConnection
    {
    struct funky_operator
    {
    friend class TcpListener;
    funky_operator(TcpConnection * c) : conn(c) {}
    private:
    void operator() (??) { ?? }
    TcpConnection * conn;
    };
    friend class funky_operator;
    funky_operator op;

    TcpConnection() : op(this) {}
    };

    Now, everyone can see "op" but only TcpListener can call it.
    Noah Roberts, Oct 2, 2011
    #6
  7. Christopher

    Noah Roberts Guest

    On Oct 1, 1:50 pm, Christopher <> wrote:
    > On Oct 1, 3:32 pm, Christopher <> wrote:
    >
    > > I know I need some sort of friend, but I don't want to friend the
    > > entire class.

    >
    > > I've got a TcpConnection class and it has a state member.
    > > I've got a TcpListener class that accepts connections and creates a
    > > TcpConnectionClass.

    >
    > > The TcpListener class needs to be able to change the state member of
    > > the TcpConnection class, but I don't want anyone else to be able to.
    > > How do I go about that?

    >
    > Problem 1 is that the first class needs to be aware of the second, so
    > when I use friend function I have a circular dependency problem when
    > keeping seperate headers.


    No you don't, because you're going to forward declare your classes
    only in headers and only include stuff you are USING in that file.
    >
    > Problem 2 is that the function I want to friend is virtual. I cannot
    > find whether or not making it a friend also makes its derived type
    > implementations of that function a friend also.


    Have the functor I described in my last post call a protected virtual
    function.
    >
    > Problem 3 is that I will also derive from the class I am allowing
    > access to.
    >           So, I want class B and all its derived types to have access
    > to the data in class A and all its derived types.


    Have a protected function in the base Listener that can be called by
    derived functions. Do your friendly access here. Your derived
    objects won't be able to access the class that friended the base
    directly.

    Of course, as others have said this is probably a bad design. It is
    certainly more than just a little coupled. But if you really need to,
    that's one way it could be done.
    Noah Roberts, Oct 2, 2011
    #7
  8. Christopher

    Paul N Guest

    On Oct 1, 9:32 pm, Christopher <> wrote:
    > I know I need some sort of friend, but I don't want to friend the
    > entire class.
    >
    > I've got a TcpConnection class and it has a state member.
    > I've got a TcpListener class that accepts connections and creates a
    > TcpConnectionClass.
    >
    > The TcpListener class needs to be able to change the state member of
    > the TcpConnection class, but I don't want anyone else to be able to.
    > How do I go about that?


    As a very low tech solution - but which may be sufficient for you...

    Why not provide a suitable access function to do what needs to be
    done, but give it a long name that includes the name of the class/
    function that you want to give access to? Any other programmers will
    see that the function exists but will realise that they're not meant
    to use it.

    Paul.
    Paul N, Oct 3, 2011
    #8
  9. Christopher

    Ian Collins Guest

    On 10/ 2/11 09:32 AM, Christopher wrote:
    > I know I need some sort of friend, but I don't want to friend the
    > entire class.
    >
    > I've got a TcpConnection class and it has a state member.
    > I've got a TcpListener class that accepts connections and creates a
    > TcpConnectionClass.
    >
    > The TcpListener class needs to be able to change the state member of
    > the TcpConnection class, but I don't want anyone else to be able to.
    > How do I go about that?


    If a design doesn't fit the problem, the design is flawed. It looks to
    me like you are over engineering what is a very simple (and common)
    task. Don't fret though, I see this all the time! Socket related
    activities simply don't fit well within an OO approach.

    --
    Ian Collins
    Ian Collins, Oct 3, 2011
    #9
  10. Christopher

    Jorgen Grahn Guest

    On Mon, 2011-10-03, Ian Collins wrote:
    > On 10/ 2/11 09:32 AM, Christopher wrote:
    >> I know I need some sort of friend, but I don't want to friend the
    >> entire class.
    >>
    >> I've got a TcpConnection class and it has a state member.
    >> I've got a TcpListener class that accepts connections and creates a
    >> TcpConnectionClass.
    >>
    >> The TcpListener class needs to be able to change the state member of
    >> the TcpConnection class, but I don't want anyone else to be able to.
    >> How do I go about that?

    >
    > If a design doesn't fit the problem, the design is flawed. It looks to
    > me like you are over engineering what is a very simple (and common)
    > task. Don't fret though, I see this all the time! Socket related
    > activities simply don't fit well within an OO approach.


    It is not clear to me what you mean with that last sentence, but I was
    going to say something similar:

    I see no great benefits of wrapping the BSD socket API in lots of stuff.
    If you wrap them a bit to get:

    - file descriptors which aren't convertible to and from ints
    - RAII

    then the API is pretty usable as it is. Bonus: anyone who knows the
    API doesn't have to relearn another one.

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
    Jorgen Grahn, Oct 3, 2011
    #10
  11. Christopher

    Ian Collins Guest

    On 10/ 4/11 09:49 AM, Jorgen Grahn wrote:
    > On Mon, 2011-10-03, Ian Collins wrote:
    >> On 10/ 2/11 09:32 AM, Christopher wrote:
    >>> I know I need some sort of friend, but I don't want to friend the
    >>> entire class.
    >>>
    >>> I've got a TcpConnection class and it has a state member.
    >>> I've got a TcpListener class that accepts connections and creates a
    >>> TcpConnectionClass.
    >>>
    >>> The TcpListener class needs to be able to change the state member of
    >>> the TcpConnection class, but I don't want anyone else to be able to.
    >>> How do I go about that?

    >>
    >> If a design doesn't fit the problem, the design is flawed. It looks to
    >> me like you are over engineering what is a very simple (and common)
    >> task. Don't fret though, I see this all the time! Socket related
    >> activities simply don't fit well within an OO approach.

    >
    > It is not clear to me what you mean with that last sentence, but I was
    > going to say something similar:


    Trying to use things like connection and listener classes!

    > I see no great benefits of wrapping the BSD socket API in lots of stuff.
    > If you wrap them a bit to get:
    >
    > - file descriptors which aren't convertible to and from ints
    > - RAII
    >
    > then the API is pretty usable as it is. Bonus: anyone who knows the
    > API doesn't have to relearn another one.


    Agreed.

    --
    Ian Collins
    Ian Collins, Oct 4, 2011
    #11
  12. Christopher

    Christopher Guest

    On Oct 3, 9:01 pm, Ian Collins <> wrote:
    > On 10/ 4/11 09:49 AM, Jorgen Grahn wrote:
    >
    >
    >
    >
    >
    > > On Mon, 2011-10-03, Ian Collins wrote:
    > >> On 10/ 2/11 09:32 AM, Christopher wrote:
    > >>> I know I need some sort of friend, but I don't want to friend the
    > >>> entire class.

    >
    > >>> I've got a TcpConnection class and it has a state member.
    > >>> I've got a TcpListener class that accepts connections and creates a
    > >>> TcpConnectionClass.

    >
    > >>> The TcpListener class needs to be able to change the state member of
    > >>> the TcpConnection class, but I don't want anyone else to be able to.
    > >>> How do I go about that?

    >
    > >> If a design doesn't fit the problem, the design is flawed.  It looksto
    > >> me like you are over engineering what is a very simple (and common)
    > >> task.  Don't fret though, I see this all the time!  Socket related
    > >> activities simply don't fit well within an OO approach.

    >
    > > It is not clear to me what you mean with that last sentence, but I was
    > > going to say something similar:

    >
    > Trying to use things like connection and listener classes!
    >
    > > I see no great benefits of wrapping the BSD socket API in lots of stuff..
    > > If you wrap them a bit to get:

    >
    > > - file descriptors which aren't convertible to and from ints
    > > - RAII

    >
    > > then the API is pretty usable as it is. Bonus: anyone who knows the
    > > API doesn't have to relearn another one.

    >
    > Agreed.
    >
    > --
    > Ian Collins- Hide quoted text -
    >
    > - Show quoted text -


    Yes, I am coming to realize the sockets just don't fit OO.
    The concept that a listener creates an accepted connection, but the
    accepted connection does the communicating is a pain in my butt.
    I've done many socket implementations before and didn't have such a
    problem with this, the added restrictions of how boost::asio::ip::tcp
    works is really adding headaches.

    Any operation gets posted for completion and calls back a given method
    when it completes. that is enforced by boost::asio::ip::tcp
    asynchronous calls.

    Well Ok, so the listener gets notified when an accept has occured.
    boo.
    Now the state of the new connection needs to change and do the things
    it does when it gets connected!
    So, either
    1) The listener has to tell the accepted connection, "you are alive
    now" or
    2) We change the callback to the connection itself, who has to tell
    the listener, "Hey, I'm alive now, do stuff with me or hand me off to
    someone"

    Either way there is a circular dependancy.
    ----
    Forward declarations to get rid of this circular dependency do not
    seem to work, because of another restriction that is a side effect
    from using boost::asio::ip::tcp.

    I must make sure that every object in my inheritance tree that will
    post an operation for io-completion, gets instantiated as a
    shared_ptr, such that the object is guarenteed to stay alive between
    the times of posting an operation and the time it completes.

    So, when it comes into the code:

    class Listener;

    Connection::Accepted(/*stuff*/)
    {
    // Snip
    listenerPtr->OnAccepted(shared_from_this());
    }

    The compiler has to see the declaration of Listener::OnAccepted, the
    Listener::SmartPtr, etc.
    ----------

    So what I've decided to do....and I hate doing it...it deriving a
    ServerSideConnection class that has the accept, onaccept methods, etc.
    and takes, upon its construction, a whole bunch of boost::functions
    that can be called back. Then when the connection gets notified of
    something, it in turn notifies the listener...This is ugly because
    I've got callback everywhere, its debugging hell, and I've got quite a
    number of connection implementations that do nothing but get notified
    and notify the listener...yuck!

    I don't see a way around it.
    ---------

    I suppose when I get time, I'll try and write up a visible and
    simplified example explaining my problems on my website and then point
    people to it for comment. It might just be too big and complex of a
    thing...and off topic...to get into the details here.
    Christopher, Oct 4, 2011
    #12
  13. Christopher

    Ian Collins Guest

    On 10/ 5/11 10:21 AM, Christopher wrote:
    >
    > So what I've decided to do....and I hate doing it...it deriving a
    > ServerSideConnection class that has the accept, onaccept methods, etc.
    > and takes, upon its construction, a whole bunch of boost::functions
    > that can be called back. Then when the connection gets notified of
    > something, it in turn notifies the listener...This is ugly because
    > I've got callback everywhere, its debugging hell, and I've got quite a
    > number of connection implementations that do nothing but get notified
    > and notify the listener...yuck!
    >
    > I don't see a way around it.


    Just use the native interface. On one of my projects we had a long
    discussion about this topic and that was our conclusion: KISS!

    --
    Ian Collins
    Ian Collins, Oct 4, 2011
    #13
  14. Christopher

    Werner Guest

    On Oct 1, 10:32 pm, Christopher <> wrote:
    > I know I need some sort of friend, but I don't want to friend the
    > entire class.
    >
    > I've got a TcpConnection class and it has a state member.
    > I've got a TcpListener class that accepts connections and creates a
    > TcpConnectionClass.
    >
    > The TcpListener class needs to be able to change the state member of
    > the TcpConnection class, but I don't want anyone else to be able to.
    > How do I go about that?


    This is how I did it:


    TcpSvrConnectionListener:

    void subscribeForConnections( IfToSvr& ifToSvr );
    void unsubscribeForConnections( IfToSvr& ifToSvr );

    IfToSvr was nested...
    class TcpSvrConnectionListener::IfToSvr
    {
    public:
    virtual void connectedEvt( SOCKET acceptDescriptor ) = 0;
    unsigned getPortNum() const{ return portNum_; }
    const std::string& getIpAddr() const { return ipAddr_; }

    protected: //Operations
    //Derivitives to specify the port number
    IfToSvr(): portNum_( 0 ), ipAddr_( "" ){ }
    IfToSvr( unsigned portNum, const std::string& ipAddr )
    : portNum_( portNum ), ipAddr_( ipAddr )
    {
    }

    //won't be deleted through this interface
    virtual ~IfToSvr(){}
    void setPortNum( unsigned portNum ){ portNum_ = portNum; }
    void setIpAddr( const std::string& ipAddr ){ ipAddr_ = ipAddr; }

    private:
    unsigned portNum_;
    std::string ipAddr_;
    };

    TcpSvrConnectionListener ran in it's own thread (listening),
    and subscription routines had to be synchronized, as well
    as connectedEvt...

    The server itself, well, it just needs to implement the
    interface and subscribe. Turned out that I separated
    the actual server from connections. The server was, on
    connectedEvt, responsible for realizing the actual
    connection. The server then used the subject observer
    pattern to dispatch the connection to all sockets
    interested. The first socket accepting the connection
    would take ownership of it, and on it being accepted,
    the server would relinquish ownership of the connection.

    Connections not accepted within a certain amount of time
    was destroyed.

    All reading was done by a service class (similar
    to what boost does) that monitors for data in a
    single thread and dispatches data to the applicable
    registrant associated with the socket on which the data
    was received (also via interface, the TcpSvrConnection
    deriving from it...).

    Contrary to what Ian Collins says, in my opinion especially
    using blocking sockets complicate user code. KISS, but not
    at the expense of usage. I haven't looked at boost sockets
    much yet, but I've looked at their usage of IO Completion
    Ports, and I like the idea of having a "service" responsible
    for listening and connecting(for clients), a "service"
    responsible for reading, as not having such a service
    complicates user code IMHO. Obviously the service class
    will be more complicated, but it has a well defined
    responsibility and usage is simple, if not opaque.

    You (as user) just create a server, associate a socket. Hookup the
    appropriate callbacks to receive data (sending is synchronous), and
    wholla. In our implementation, the service class has to exist
    prior to this, but enabling of the service classes are done
    from a central location. One could instantiate the service classes
    when needed. We kept that part simple :) and opted for explicit
    creation upfront.

    We've created interfaces to break dependencies and to communicate
    responsibilities of classes in terms of each other.

    Good luck :),

    Kind regards,

    Werner
    Werner, Oct 5, 2011
    #14
  15. Christopher

    Jorgen Grahn Guest

    On Tue, 2011-10-04, Ian Collins wrote:
    > On 10/ 5/11 10:21 AM, Christopher wrote:
    >>
    >> So what I've decided to do....and I hate doing it...it deriving a
    >> ServerSideConnection class that has the accept, onaccept methods, etc.
    >> and takes, upon its construction, a whole bunch of boost::functions
    >> that can be called back. Then when the connection gets notified of
    >> something, it in turn notifies the listener...This is ugly because
    >> I've got callback everywhere, its debugging hell, and I've got quite a
    >> number of connection implementations that do nothing but get notified
    >> and notify the listener...yuck!
    >>
    >> I don't see a way around it.

    >
    > Just use the native interface. On one of my projects we had a long
    > discussion about this topic and that was our conclusion: KISS!


    I'm repeating myself, but an anecdote:

    A few years back I decided to write The One True C++ Wrapper For BSD
    Sockets. I wanted type safety: distinct types for UDP sockets, TCP
    listening sockets, "normal" TCP sockets ... with the type system
    preventing mistakes such as doing listen() on an UDP socket or setting
    UDP-specific socket options on a TCP socket. But at the same time I
    didn't want to invent a brand new interface to learn.

    After a while I realized that (a) this was hard work and (b) it was
    for the most part not useful work. Any half-decent design in your
    application makes most of the mistakes I wanted to eliminate
    - unlikely to happen, because you tend to keep different kinds of
    sockets apart
    - likely to be found quickly if they /did/ happen

    So I threw most of it away. I just kept some read/write buffering code
    which had proven useful in real life.

    (Disclaimer: I have no experience with Boost.asio; perhaps it forces you
    in another direction.)

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
    Jorgen Grahn, Oct 5, 2011
    #15
  16. Christopher

    Ian Collins Guest

    On 10/ 5/11 09:19 PM, Jorgen Grahn wrote:
    > On Tue, 2011-10-04, Ian Collins wrote:
    >>
    >> Just use the native interface. On one of my projects we had a long
    >> discussion about this topic and that was our conclusion: KISS!

    >
    > I'm repeating myself, but an anecdote:
    >
    > A few years back I decided to write The One True C++ Wrapper For BSD
    > Sockets. I wanted type safety: distinct types for UDP sockets, TCP
    > listening sockets, "normal" TCP sockets ... with the type system
    > preventing mistakes such as doing listen() on an UDP socket or setting
    > UDP-specific socket options on a TCP socket. But at the same time I
    > didn't want to invent a brand new interface to learn.
    >
    > After a while I realized that (a) this was hard work and (b) it was
    > for the most part not useful work. Any half-decent design in your
    > application makes most of the mistakes I wanted to eliminate
    > - unlikely to happen, because you tend to keep different kinds of
    > sockets apart
    > - likely to be found quickly if they /did/ happen
    >
    > So I threw most of it away. I just kept some read/write buffering code
    > which had proven useful in real life.


    At least you had the good sense to give up after only one attempt. I
    had three goes, over 15 years before realising the error of my ways.

    > (Disclaimer: I have no experience with Boost.asio; perhaps it forces you
    > in another direction.)


    I've only ever seen it end in tears.

    --
    Ian Collins
    Ian Collins, Oct 5, 2011
    #16
  17. Christopher

    Miles Bader Guest

    Ian Collins <> writes:
    >> So I threw most of it away. I just kept some read/write buffering code
    >> which had proven useful in real life.

    >
    > At least you had the good sense to give up after only one attempt. I
    > had three goes, over 15 years before realising the error of my ways.


    Ah well, such things tend to be a learning experience, at least ...

    -miles

    --
    "Whatever you do will be insignificant, but it is very important that
    you do it." Mahatma Gandhi
    Miles Bader, Oct 5, 2011
    #17
  18. Christopher

    Jorgen Grahn Guest

    On Wed, 2011-10-05, Ian Collins wrote:
    > On 10/ 5/11 09:19 PM, Jorgen Grahn wrote:

    ....
    >> So I threw most of it away. I just kept some read/write buffering code
    >> which had proven useful in real life.

    >
    > At least you had the good sense to give up after only one attempt. I
    > had three goes, over 15 years before realising the error of my ways.


    Well, ask me again 15 years from now ;-)

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
    Jorgen Grahn, Oct 5, 2011
    #18
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Sam Iam
    Replies:
    0
    Views:
    446
    Sam Iam
    Jan 31, 2004
  2. Howard
    Replies:
    2
    Views:
    375
    Michiel Salters
    Jul 4, 2003
  3. Rolf Magnus
    Replies:
    1
    Views:
    325
    ding feng
    Jul 4, 2003
  4. liking C lang.

    May output the char one after one every other second?

    liking C lang., Feb 16, 2007, in forum: C Programming
    Replies:
    16
    Views:
    551
    Keith Thompson
    Feb 17, 2007
  5. Pankaj Garg
    Replies:
    1
    Views:
    214
    Michelle Hlaing
    Feb 24, 2005
Loading...

Share This Page