Is C++ really portable?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Siemel Naran

    #16
    Re: Is C++ really portable?

    "DeMarcus" <nobody@tellus. orb> wrote in message news:40899cab$0 $99624
    [color=blue]
    > Thank all of you for all your answers, I really
    > appreciate it. But there's one very imporatant
    > thing that I might have been a little vague about,
    > and that is the importance of not having to
    > recompile the application because of a library
    > change. Therefore #ifdef won't do.[/color]

    An ifdef in the cpp file will work. But then you're using the pointer to
    implementation idea, so you pay the cost of a non-inline function call.
    [color=blue]
    > Suppose you sell your application to someone and
    > this guy in turn buys a plugin to it from a third
    > party vendor, where this plugin only supports
    > "strangeThr ead" but not the apps default pthread.
    > Then it would be neat to just send him a new
    > strangeThread.s o or .dll to put in a specific
    > directory and suddenly it works.
    >
    > It's feasible with the design patterns 'abstract
    > factory' or 'bridge', but both of them result in
    > an overhead with pointer referencing and memory
    > allocation. I just wanted that C++ could do it
    > anyway, but the only solution I can see right now
    > is this solution[/color]

    Have you actually measured it to determine it is a bottleneck? How many
    MyThread objects do you create? How does the time spent in creating the
    DerivedMyThread object and calling its virtual functions compare to the time
    spent in the rest of the application? It might not be anything at all.
    [color=blue]
    > class MyThread
    > {
    > public:
    > void start();
    > ...
    >
    > private:
    >
    > char varStore[ BIG_ENOUGH_TO_H OLD_THE_BIGGEST _
    > CLASS_DECLARATI ON_OF_ALL_POSSI BLE_IMPLEMENTAT IONS ];
    > }[/color]

    The above strikes me as a valid use of reinterpret_cas t in general, but only
    a last resort if profiling suggests it. Pay attention to alignment issues
    though. The char type usually has alignment 1, whereas pthread_t may have
    alignment 4. To solve, there is no perfect way, but you can create a union
    of all fundamental types along with your varStore[...] variable. The
    union's size will be at least varStore[...] and will be properly aligned.
    [color=blue]
    > The header above could be used by anyone compiling
    > the application or third party plugins.
    >
    > // Here's two different examples of implementation
    > void MyThread::start ()
    > {
    > pthread_create( (pthread_t*)var Store, ... );
    > }
    >
    > void MyThread::start ()
    > {
    > strange_thread_ create( (strange_thread _t*)varStore, ... );
    > }[/color]

    How does the cost of a factory compare to the cost of pthread_create?

    To make using the factory even faster you can overload MyThread::opera tor
    new and use pool allocation. But how many threads do you allocate anyway?
    [color=blue]
    > If I do this dirty implementation then I can compile
    > the application once and hope and pray nobody makes
    > a thread library with a bigger class declaration than
    > BIG_ENOUGH_... that the application was compiled with.[/color]

    You don't have to pray for this. You can use a compile time assert to
    assert that sizeof(actual_t hread) <= sizeof(varStore ). These compile time
    asserts work by creating an array of size zero if the condition is false,
    and such a thing fails compilation.
    [color=blue]
    > My hope here was that the C++ compiler could take care
    > of that somehow, but I realize it's almost impossible.[/color]

    Not sure if any compiler can do what you're asking for.


    Comment

    • Gianni Mariani

      #17
      Re: Is C++ really portable?

      DeMarcus wrote:[color=blue]
      >
      >
      > Thank all of you for all your answers, I really
      > appreciate it. But there's one very imporatant
      > thing that I might have been a little vague about,
      > and that is the importance of not having to
      > recompile the application because of a library
      > change. Therefore #ifdef won't do.
      >
      > Suppose you sell your application to someone and
      > this guy in turn buys a plugin to it from a third
      > party vendor, where this plugin only supports
      > "strangeThr ead" but not the apps default pthread.
      > Then it would be neat to just send him a new
      > strangeThread.s o or .dll to put in a specific
      > directory and suddenly it works.
      >
      > It's feasible with the design patterns 'abstract
      > factory' or 'bridge', but both of them result in
      > an overhead with pointer referencing and memory
      > allocation. I just wanted that C++ could do it
      > anyway, but the only solution I can see right now
      > is this solution
      >
      > class MyThread
      > {
      > public:
      > void start();
      > ...
      >
      > private:
      >
      > char varStore[ BIG_ENOUGH_TO_H OLD_THE_BIGGEST _
      > CLASS_DECLARATI ON_OF_ALL_POSSI BLE_IMPLEMENTAT IONS ];
      > }
      >[/color]

      don't use 'char' - use somthing with the largest alignment
      of any of the implemntations you require.

      Also, on some systems, a virtual function call is just as fast
      as a regular call.

      Finally, one possibility is to go ahead and use a factory
      but make it so the factory puts the function addresses
      directly in the structure.

      Somthing like:

      class MyThread
      {
      friend class ThreadImpl;

      void ( * StartFuncPtr )( MyThread * );
      void ( * CleanupFuncPtr )( MyThread * );

      long long var_store[ NUM_BIG_ENUFF ];

      public:

      inline Start()
      {
      StartFuncPtr( this );
      }

      virtual ~MyThread()
      {
      CleanupFuncPtr( this );
      }

      MyThread()
      {
      // factory initialize
      ThreadFactoryIn itializeNew( this );
      }

      // application overrides this function
      virtual DoStuff() = 0;
      };

      [color=blue]
      > The header above could be used by anyone compiling
      > the application or third party plugins.
      >
      > // Here's two different examples of implementation
      > void MyThread::start ()
      > {
      > pthread_create( (pthread_t*)var Store, ... );
      > }
      >
      > void MyThread::start ()
      > {
      > strange_thread_ create( (strange_thread _t*)varStore, ... );
      > }
      >
      > If I do this dirty implementation then I can compile
      > the application once and hope and pray nobody makes
      > a thread library with a bigger class declaration than
      > BIG_ENOUGH_... that the application was compiled with.
      >
      > My hope here was that the C++ compiler could take care
      > of that somehow, but I realize it's almost impossible.[/color]

      It's very possible. You can also make it very safe by making the ugly
      nasty stuff private so that nothing else messes with the bits that they
      should not.


      Comment

      • Krzysztof Zelechowski

        #18
        Re: Is C++ really portable?


        Uzytkownik "DeMarcus" <nobody@tellus. orb> napisal w wiadomosci
        news:40899cab$0 $99624$57c3e1d3 @news3.bahnhof. se...[color=blue]
        >
        >
        > Suppose you sell your application to someone and
        > this guy in turn buys a plugin to it from a third
        > party vendor, where this plugin only supports
        > "strangeThr ead" but not the apps default pthread.
        > Then it would be neat to just send him a new
        > strangeThread.s o or .dll to put in a specific
        > directory and suddenly it works.
        >[/color]
        I think the plugin must be appropriate for your application and not the
        opposite.


        Comment

        • DeMarcus

          #19
          Re: Is C++ really portable?



          [color=blue][color=green]
          >>
          >>It's feasible with the design patterns 'abstract
          >>factory' or 'bridge', but both of them result in
          >>an overhead with pointer referencing and memory
          >>allocation. I just wanted that C++ could do it
          >>anyway, but the only solution I can see right now
          >>is this solution[/color]
          >
          >
          > Have you actually measured it to determine it is a bottleneck? How many
          > MyThread objects do you create? How does the time spent in creating the
          > DerivedMyThread object and calling its virtual functions compare to the time
          > spent in the rest of the application? It might not be anything at all.
          >[/color]

          Maybe you're right here, I may not lose much. I know that one shall
          never begin with optimizing, but in this case where it is the structure
          of the program it feels like if I do this wrong there will be no space
          for optimizations later.

          I was just worried about all the mutexes that will go from
          myMutex.lock();
          to
          myMutex->lock();
          (virtual)

          I hope it won't be a too big performance hit.
          [color=blue]
          >
          > To make using the factory even faster you can overload MyThread::opera tor
          > new and use pool allocation. But how many threads do you allocate anyway?
          >
          >[/color]

          I really like that pool allocation idea! That's an area where I want to
          improve my programming skills. Do you know where to find exhaustive
          information about creating rock solid buffer pools?

          [color=blue][color=green]
          >>If I do this dirty implementation then I can compile
          >>the application once and hope and pray nobody makes
          >>a thread library with a bigger class declaration than
          >>BIG_ENOUGH_.. . that the application was compiled with.[/color]
          >
          >
          > You don't have to pray for this. You can use a compile time assert to
          > assert that sizeof(actual_t hread) <= sizeof(varStore ). These compile time
          > asserts work by creating an array of size zero if the condition is false,
          > and such a thing fails compilation.
          >[/color]

          That's something I didn't know about. Do you know where I can read
          more about it?


          Comment

          • DeMarcus

            #20
            Re: Is C++ really portable?



            Krzysztof Zelechowski wrote:
            [color=blue]
            > Uzytkownik "DeMarcus" <nobody@tellus. orb> napisal w wiadomosci
            > news:40899cab$0 $99624$57c3e1d3 @news3.bahnhof. se...
            >[color=green]
            >>
            >>Suppose you sell your application to someone and
            >>this guy in turn buys a plugin to it from a third
            >>party vendor, where this plugin only supports
            >>"strangeThrea d" but not the apps default pthread.
            >>Then it would be neat to just send him a new
            >>strangeThread .so or .dll to put in a specific
            >>directory and suddenly it works.
            >>[/color]
            >
            > I think the plugin must be appropriate for your application and not the
            > opposite.
            >
            >[/color]

            When you're Big Buck Inc. yes. But I'm not. ;) Therefore I will be the
            only one losing if the application doesn't work as the customer
            expected. If he goes to Big Buck Third Party Plugin Manufacturer Inc.
            and ask them to change their plugins they will say they wait until my
            app is working properly.

            But you're right. This is actually a catch22 situation that probably
            will never happen. The thing is that I personaly want to use pthreads
            meanwhile many companies in the business use an older variant of
            threads, so I need a simple way to deal with both.




            Comment

            • Jakob Bieling

              #21
              Re: Is C++ really portable?

              "DeMarcus" <nobody@tellus. orb> wrote in message
              news:40899cab$0 $99624$57c3e1d3 @news3.bahnhof. se...
              [color=blue]
              > Thank all of you for all your answers, I really
              > appreciate it. But there's one very imporatant
              > thing that I might have been a little vague about,
              > and that is the importance of not having to
              > recompile the application because of a library
              > change. Therefore #ifdef won't do.[/color]

              But will you not have to recompile anyway, since you cannot use the
              Linux executable on a Windows system anyway?
              [color=blue]
              > Suppose you sell your application to someone and
              > this guy in turn buys a plugin to it from a third
              > party vendor, where this plugin only supports
              > "strangeThr ead" but not the apps default pthread.
              > Then it would be neat to just send him a new
              > strangeThread.s o or .dll to put in a specific
              > directory and suddenly it works.[/color]

              Well, think about it. That plugin is written after you ceated your app,
              so who would ever create a plugin that does not work with the app? Sure, you
              do restrict plugin writers to use a specific lib, but that is life ;)
              [color=blue]
              > It's feasible with the design patterns 'abstract
              > factory' or 'bridge', but both of them result in
              > an overhead with pointer referencing and memory
              > allocation. I just wanted that C++ could do it
              > anyway, but the only solution I can see right now
              > is this solution[/color]

              As I mentioned in another post, you just create an interface and let the
              libraries contain a derived class from that interface and let it do all the
              implementation specific stuff. Then you have the dynamic lib export two
              functions: create_thread and destroy_thread. In the former you allocate the
              derived class with new and return the pointer, in the latter you destroy it
              again. The overhead of allocating from the heap will surely be neglectable,
              because it is rather unlikely that you will create hundrets of threads ..

              hth
              --
              jb

              (replace y with x if you want to reply by e-mail)


              Comment

              • Siemel Naran

                #22
                Re: Is C++ really portable?

                "Gianni Mariani" <gi2nospam@mari ani.ws> wrote in message
                [color=blue]
                > Also, on some systems, a virtual function call is just as fast
                > as a regular call.[/color]

                How can this be? You have to look up the address of a function in the
                virtual table and then call it, so logic suggests it has to be slower. But
                maybe I'm overlooking something?




                Comment

                • Siemel Naran

                  #23
                  Re: Is C++ really portable?

                  "DeMarcus" <nobody@tellus. orb> wrote in message news:408a2724$0 $99624
                  [color=blue]
                  > I really like that pool allocation idea! That's an area where I want to
                  > improve my programming skills. Do you know where to find exhaustive
                  > information about creating rock solid buffer pools?
                  >
                  >[color=green]
                  > > You don't have to pray for this. You can use a compile time assert to
                  > > assert that sizeof(actual_t hread) <= sizeof(varStore ). These compile[/color][/color]
                  time[color=blue][color=green]
                  > > asserts work by creating an array of size zero if the condition is[/color][/color]
                  false,[color=blue][color=green]
                  > > and such a thing fails compilation.
                  > >[/color]
                  >
                  > That's something I didn't know about. Do you know where I can read
                  > more about it?[/color]

                  Try google, google newsgroups, browse the boost libraries, etc. Sorry, have
                  to catch a flight now.


                  Comment

                  • Daniel T.

                    #24
                    Re: Is C++ really portable?

                    DeMarcus <nobody@tellus. orb> wrote:
                    [color=blue]
                    > Let's say we're gonna make a system library and have a class with
                    > two prerequisites.
                    >
                    > * It will be used _a_lot_, and therefore need to be really efficient.
                    > * It can have two different implementations . (e.g. Unix/Windows)
                    >
                    > I feel stuck. The only solution I've seen so far is using the
                    > design pattern 'abstract factory' that gives me a pointer to a pure
                    > virtual interface (which can have whatever implementation) . But that
                    > forces me to make a memory allocation every time I need an instance
                    > of that class! That's all but efficient.
                    >
                    > Do I have to live with this? Or do I have to make some kind of
                    >
                    > class SystemThing
                    > {
                    > ...
                    > private:
                    >
                    > strange_unix_va r uv;
                    > strange_win_var wv;
                    > }
                    >
                    > and then use the appropriate variable in the library implementation?
                    >
                    > How is this problem commonly solved?[/color]

                    Part of my job has been to ensure that the code written by the rest of
                    the people on the team is portable between Windows and MacOS X. Here is
                    how I solve the problem:

                    class GenericThingTha tsImplementedDi fferently {
                    class Impl;
                    Impl* pimpl;
                    public:
                    void genericFunction ();
                    };

                    Then I will write two or three source files, one would be Windows
                    spicific code, one is Mac spicific code, and the last (optional one)
                    would be for non-spicific code.

                    Comment

                    • Gianni Mariani

                      #25
                      Re: Is C++ really portable?

                      Siemel Naran wrote:[color=blue]
                      > "Gianni Mariani" <gi2nospam@mari ani.ws> wrote in message
                      >
                      >[color=green]
                      >>Also, on some systems, a virtual function call is just as fast
                      >>as a regular call.[/color]
                      >
                      >
                      > How can this be? You have to look up the address of a function in the
                      > virtual table and then call it, so logic suggests it has to be slower. But
                      > maybe I'm overlooking something?[/color]

                      CPU's today are very advanced and many things that made sense even 10
                      years ago, no longer are true. The biggest effects come from multiple
                      issue, speculative execution, cache effects and branch prediction.

                      For example - look at this code:


                      class foo
                      {
                      public:
                      virtual bool X() const = 0;

                      };

                      bool Y( void * v);

                      int DY( void ** v)
                      {
                      Y( * v );
                      return 0;
                      }

                      int Dfoo( const foo ** v)
                      {
                      (*v)->X();
                      return 0;
                      }


                      $ g++ -mtune=pentium4 -c -o xx.o xx.cpp -O3
                      $ objdump --disassemble xx.o

                      xx.o: file format elf32-i386

                      Disassembly of section .text:

                      00000000 <_Z2DYPPv>:
                      0: 55 push %ebp
                      1: 89 e5 mov %esp,%ebp
                      3: 83 ec 08 sub $0x8,%esp
                      6: 8b 55 08 mov 0x8(%ebp),%edx
                      9: 8b 02 mov (%edx),%eax
                      b: 89 04 24 mov %eax,(%esp,1)
                      e: e8 fc ff ff ff call f <_Z2DYPPv+0xf >
                      13: 31 c0 xor %eax,%eax
                      15: c9 leave
                      16: c3 ret
                      17: 90 nop

                      00000018 <_Z4DfooPPK3foo >:
                      18: 55 push %ebp
                      19: 89 e5 mov %esp,%ebp
                      1b: 83 ec 08 sub $0x8,%esp
                      1e: 8b 4d 08 mov 0x8(%ebp),%ecx
                      21: 8b 01 mov (%ecx),%eax
                      23: 8b 10 mov (%eax),%edx
                      25: 89 04 24 mov %eax,(%esp,1)
                      28: ff 12 call *(%edx)
                      2a: 31 c0 xor %eax,%eax
                      2c: c9 leave
                      2d: c3 ret

                      Note that the code size for the virtual call version is *shorter* than
                      the non virtual call. On a multiple issue machine, both would execute at
                      in the same number of cycles.


                      On an Athlon64 machine:

                      $ g++ -m64 -c -o xx.o xx.cpp -O3
                      $ objdump --disassemble xx.o

                      xx.o: file format elf64-x86-64

                      Disassembly of section .text:

                      000000000000000 0 <_Z2DYPPv>:
                      0: 48 83 ec 08 sub $0x8,%rsp
                      4: 48 8b 3f mov (%rdi),%rdi
                      7: e8 00 00 00 00 callq c <_Z2DYPPv+0xc >
                      c: 48 83 c4 08 add $0x8,%rsp
                      10: 31 c0 xor %eax,%eax
                      12: c3 retq
                      13: 90 nop
                      14: 66 data16
                      15: 66 data16
                      16: 66 data16
                      17: 90 nop
                      18: 66 data16
                      19: 66 data16
                      1a: 66 data16
                      1b: 90 nop
                      1c: 66 data16
                      1d: 66 data16
                      1e: 66 data16
                      1f: 90 nop

                      000000000000002 0 <_Z4DfooPPK3foo >:
                      20: 48 83 ec 08 sub $0x8,%rsp
                      24: 48 8b 3f mov (%rdi),%rdi
                      27: 48 8b 07 mov (%rdi),%rax
                      2a: ff 10 callq *(%rax)
                      2c: 48 83 c4 08 add $0x8,%rsp
                      30: 31 c0 xor %eax,%eax
                      32: c3 retq

                      Note that the code size again for the virtual call version is *shorter*
                      than the non virtual call. Note also that the compiler generated a 32
                      bit instruction for the non-virtual call. On some other erchitectures,
                      a 64 bit call means loading 2 32 bit values into 2 separate registers
                      and combining them to create a 64 bit address (mips n64 is an example).

                      I don't have access to an Itanium compiler but I suspect that it is even
                      more telling.

                      In summary, it depends on your code and the architecture, but there are
                      examples where it can be faster to use virtual functions compared to
                      non-virtual functions.

                      The only time it's interesting to use non-virtual functions is where
                      it's possible to use inlining. As for the OP, inlining is out of the
                      question.



                      Comment

                      • DeMarcus

                        #26
                        Re: Is C++ really portable?



                        Daniel T. wrote:[color=blue]
                        > DeMarcus <nobody@tellus. orb> wrote:
                        >
                        >[color=green]
                        >>Let's say we're gonna make a system library and have a class with
                        >>two prerequisites.
                        >>
                        >>* It will be used _a_lot_, and therefore need to be really efficient.
                        >>* It can have two different implementations . (e.g. Unix/Windows)
                        >>
                        >>I feel stuck. The only solution I've seen so far is using the
                        >>design pattern 'abstract factory' that gives me a pointer to a pure
                        >>virtual interface (which can have whatever implementation) . But that
                        >>forces me to make a memory allocation every time I need an instance
                        >>of that class! That's all but efficient.
                        >>
                        >>Do I have to live with this? Or do I have to make some kind of
                        >>
                        >>class SystemThing
                        >>{
                        >>...
                        >>private:
                        >>
                        >> strange_unix_va r uv;
                        >> strange_win_var wv;
                        >>}
                        >>
                        >>and then use the appropriate variable in the library implementation?
                        >>
                        >>How is this problem commonly solved?[/color]
                        >
                        >
                        > Part of my job has been to ensure that the code written by the rest of
                        > the people on the team is portable between Windows and MacOS X. Here is
                        > how I solve the problem:
                        >
                        > class GenericThingTha tsImplementedDi fferently {
                        > class Impl;
                        > Impl* pimpl;
                        > public:
                        > void genericFunction ();
                        > };
                        >
                        > Then I will write two or three source files, one would be Windows
                        > spicific code, one is Mac spicific code, and the last (optional one)
                        > would be for non-spicific code.[/color]

                        Yes, I think this pattern is what they call a 'bridge' and it looks like
                        it will be the solution I go for.




                        Comment

                        • Daniel T.

                          #27
                          Re: Is C++ really portable?

                          In article <408CE45B.40805 07@tellus.orb>, DeMarcus <nobody@tellus. orb>
                          wrote:
                          [color=blue]
                          > Daniel T. wrote:[color=green]
                          > > DeMarcus <nobody@tellus. orb> wrote:
                          > >
                          > >[color=darkred]
                          > >>Let's say we're gonna make a system library and have a class with
                          > >>two prerequisites.
                          > >>
                          > >>* It will be used _a_lot_, and therefore need to be really efficient.
                          > >>* It can have two different implementations . (e.g. Unix/Windows)
                          > >>
                          > >>I feel stuck. The only solution I've seen so far is using the
                          > >>design pattern 'abstract factory' that gives me a pointer to a pure
                          > >>virtual interface (which can have whatever implementation) . But that
                          > >>forces me to make a memory allocation every time I need an instance
                          > >>of that class! That's all but efficient.
                          > >>
                          > >>Do I have to live with this? Or do I have to make some kind of
                          > >>
                          > >>class SystemThing
                          > >>{
                          > >>...
                          > >>private:
                          > >>
                          > >> strange_unix_va r uv;
                          > >> strange_win_var wv;
                          > >>}
                          > >>
                          > >>and then use the appropriate variable in the library implementation?
                          > >>
                          > >>How is this problem commonly solved?[/color]
                          > >
                          > >
                          > > Part of my job has been to ensure that the code written by the rest of
                          > > the people on the team is portable between Windows and MacOS X. Here is
                          > > how I solve the problem:
                          > >
                          > > class GenericThingTha tsImplementedDi fferently {
                          > > class Impl;
                          > > Impl* pimpl;
                          > > public:
                          > > void genericFunction ();
                          > > };
                          > >
                          > > Then I will write two or three source files, one would be Windows
                          > > spicific code, one is Mac spicific code, and the last (optional one)
                          > > would be for non-spicific code.[/color]
                          >
                          > Yes, I think this pattern is what they call a 'bridge' and it looks like
                          > it will be the solution I go for.[/color]

                          It looks like a bridge but it really isn't. In the Bridgle pattern, the
                          Implementor can be different for different Abstraction objects within
                          the same program (in fact the Implementor can be changed out for a
                          particular Abstraction object at runtime,) in the above all the
                          Abstraction objects must use the same Implementor.

                          Comment

                          Working...