IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Comment passer un pointeur (d'un std::unique_ptr) à une fonction template ?


Sujet :

Langage C++

  1. #21
    Invité
    Invité(e)
    Par défaut
    Non, je ne pense pas que ça soit en dehors des contraintes car, j'ai besoin de modifier l'adresse du pointeur possédé par mon std::unique_ptr via une fonction sans type de retour qui prend une référence sur le pointeur possédé par mon std::unique_ptr (cette fonction modifie aussi l'adresse des pointeurs nu lors de la lecture de données)

    Hors std::unique_ptr ne permet pas de le faire et std::shared_pointer je ne pense pas non plus.
    Même chose si je veux modifier l'adresse d'un tableau avec une fonction sans type de retour, j'ai essayé par tout les moyen mais ça me renvoie à chaque fois à une erreur en compilation!

    Non sincèrement, je n'ai pas trouvé d'autre solution que de faire ma propre classe pour gérer des pointeurs intelligents :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    687
    688
    689
    690
    691
    692
    693
    694
    695
    696
    697
    698
    699
    700
    701
    702
    703
    704
    705
    706
    707
    708
    709
    710
    711
    712
    713
    714
    715
    716
    717
    718
    719
    720
    721
    722
    723
    724
    725
    726
    727
    728
    729
    730
    731
    732
    733
    734
    735
    736
    737
    738
    739
    740
    741
    742
    743
    744
    745
    746
    747
    748
    749
    750
    751
    752
    753
    754
    755
    756
    757
    758
    759
    760
    761
    762
    763
    764
    765
    766
    767
    768
    769
    770
    771
    772
    773
    774
    775
    776
    777
    778
    779
    780
    781
    782
    783
    784
    785
    786
    787
    788
    789
    790
    791
    792
    793
    794
    795
    796
    797
    798
    799
    800
    801
    802
    803
    804
    805
    806
    807
    808
    809
    810
    811
    812
    813
    814
    815
    816
    817
    818
    819
    820
    821
    822
    823
    824
    825
    826
    827
    828
    829
    830
    831
    832
    833
    834
    835
    836
    837
    838
    839
    840
    841
    842
    843
    844
    845
    846
    847
    848
    849
    850
    851
    852
    853
    854
    855
    856
    857
    858
    859
    860
    861
    862
    863
    864
    865
    866
    867
    868
    869
    870
    871
    872
    873
    874
    875
    876
    877
    878
    879
    880
    881
    882
    883
    884
    885
    886
    887
    888
    889
    890
    891
    892
    893
    894
    895
    896
    897
    898
    899
    900
    901
    902
    903
    904
    905
    906
    907
    908
    909
    910
    911
    912
    913
    914
    915
    916
    917
    918
    919
    920
    921
    922
    923
    924
    925
    926
    927
    928
    929
    930
    931
    932
    933
    934
    935
    936
    937
    938
    939
    940
    941
    942
    943
    944
    945
    946
    947
    948
    949
    950
    951
    952
    953
    954
    955
    956
    957
    958
    959
    960
    961
    962
    963
    964
    965
    966
    967
    968
    969
    970
    971
    972
    973
    974
    975
    976
    977
    978
    979
    980
    981
    982
    983
    984
    985
    986
    987
    988
    989
    990
    991
    992
    993
    994
    995
    996
    997
    998
    999
    1000
    1001
    1002
    1003
    1004
    1005
    1006
    1007
    1008
    1009
    1010
    1011
    1012
    1013
    1014
    1015
    1016
    1017
    1018
     
    #ifndef ODFAEG_ARCHIVE
    #define ODFAEG_ARCHIVE
    #include <vector>
    #include <map>
    #include <iostream>
    #include <typeinfo>
    #include "factory.h"
    #include <sstream>
    #include <memory>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include "resourceCache.h"
    /**
      *\namespace odfaeg
      * the namespace of the Opensource Development Framework Adapted for Every Games.
      */
    namespace odfaeg {
    /**
    * maximum decompressed data input length (1MB)
    */
    #define MAX_DECOMPRESSED_LENGTH 1048576
     
    /**
    * maximum compressed data input length
    */
    #define MAX_COMPRESSED_LENGTH (MAX_DECOMPRESSED_LENGTH + (MAX_DECOMPRESSED_LENGTH/2))
    /**
    * hash table length
    */
    #define DICT_LEN 16384
     
    #define DecodeOffset(x) DecodeLength(x)
    /**
    * utf16 character
    */
    typedef unsigned short u16;
     
    /**
    * one byte
    */
    typedef unsigned char u8;
     
    /**
    * 32 bit unsigned integer
    */
    typedef unsigned int u32;
    template <typename T>
    struct has_typedef_key {
        // Types "yes" and "no" are guaranteed to have different sizes,
        // specifically sizeof(yes) == 1 and sizeof(no) == 2.
        typedef char yes[1];
        typedef char no[2];
     
        template <typename C>
        static yes& test(typename C::KEYTYPE*);
        template <typename>
        static no& test(...);
     
        // If the "sizeof" of the result of calling test<T>(0) would be equal to sizeof(yes),
        // the first overload worked and T has a nested type named foobar.
        static const int value = sizeof(test<T>(0)) == sizeof(yes);
    };
    /**
    * \file archive.h
    * \class OTextArchive
    * \brief Write everything into the output archive's buffer in text format.
    * \author Duroisin.L
    * \version 1.0
    * \date 1/02/2014
    */
    class OTextArchive {
    public :
        /**\fn OTextArchive (std::ostream& buffer)
        *  \brief pass a c++ ouptut buffer to the archive.
        *  \param std::ostream& buffer : the output buffer.
        */
        OTextArchive(std::ostream& buffer) : buffer(buffer) {
            nbSerialized = 0;
        }
        /** \fn void compress_stdin
        *   \brief compress the data contained into the ouptut archive's buffer.
        */
        void compress_stdin(void)
        {
          std::ostringstream output;
          output<<buffer;
          int i, out_len;
          char *buffer, *output_buffer;
          i = output.str().size();
          /* allocate input and output buffers */
          buffer = new char[MAX_DECOMPRESSED_LENGTH];
          output_buffer = new char [MAX_COMPRESSED_LENGTH];
     
          /* read input from stdin */
          if (i == MAX_DECOMPRESSED_LENGTH)
          {
            std::cerr<<"ERROR: Input is too long!"<<std::endl;
            return;
          }
          buffer = const_cast<char*> (output.str().c_str());
          output.str("");
     
          /* each utf16 character is 2 bytes long so the input number of bytes must be odd */
          if (i % 2 != 0)
            fprintf(stderr, "WARNING: odd number of input bytes!\n");
     
          /* compress */
          out_len = utf16_compress((u16 *)buffer, i / 2, (u8 *)output_buffer);
     
          //delete[] buffer;
          this->buffer.rdbuf()->pubsetbuf(output_buffer, out_len);
          //delete[] output_buffer;
        }
        //Fundamentals.
        /**
        * \fn void operator(T& data, D...)
        * \brief write a fundamental type into the archive.
        * \param T& the data to write.
        * \param D... used for SFINAE.
        */
        template <typename T,
                  class... D,
                  class = typename std::enable_if<std::is_fundamental<T>::value>::type>
        void operator() (T& data, D...) {
            buffer<<data<<std::endl;
        }
        /**
        * \fn void operator(T* data, D...)
        * \brief write pointer to a fundamental type into the archive.
        * \param T* the pointer to write.
        * \param D... used for SFINAE.
        */
        template <typename T,
              class... D,
              class = typename std::enable_if<std::is_fundamental<T>::value>::type>
        void operator() (T* data, D...) {
            if (data != nullptr) {
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(reinterpret_cast<unsigned long long int>(data));
                if (it != adresses.end()) {
                    buffer<<it->second<<std::endl;
                } else {
                    std::pair<unsigned long long int, unsigned long long int> newAddress (reinterpret_cast<unsigned long long int>(data), nbSerialized);
                    adresses.insert(newAddress);
                    buffer<<newAddress.second<<std::endl;
                    (*this)(*data);
                    nbSerialized++;
                }
            } else {
                int id = -1;
                buffer<<id<<std::endl;
            }
        }
        template <typename E,
        class... D,
        class = typename std::enable_if<!std::is_fundamental<E>::value>::type,
        class = typename std::enable_if<std::is_enum<E>::value>::type>
        void operator() (E& data, D...) {
            buffer<<data<<std::endl;
        }
        template <typename E,
        class... D,
        class = typename std::enable_if<!std::is_fundamental<D>::value>::type,
        class = typename std::enable_if<std::is_enum<E>::value>::type>
        void operator() (E* data, D...) {
            if (data != nullptr) {
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(reinterpret_cast<unsigned long long int>(data));
                if (it != adresses.end()) {
                    buffer<<it->second<<std::endl;
                } else {
                    std::pair<unsigned long long int, unsigned long long int> newAddress (reinterpret_cast<unsigned long long int>(data), nbSerialized);
                    adresses.insert(newAddress);
                    buffer<<newAddress.second<<std::endl;
                    (*this)(*data);
                    nbSerialized++;
                }
            } else {
                int id = -1;
                buffer<<id<<std::endl;
            }
        }
        //std::string.
        /**
        *\fn void operator(T& data, D...)
        *\brief write an std::string into the archive.
        *\param T& data : the std::string to write.
        *\param D... : used fo SFINAE.
        */
        template <typename T,
              class... D,
              class = typename std::enable_if<!std::is_fundamental<T>::value>::type,
              class = typename std::enable_if<std::is_same<T, std::string>::value>::type,
              class = typename std::enable_if<!std::is_enum<T>::value>::type>
        void operator() (T& data, D...) {
             std::size_t str_size = data.length();
             const char* datas = data.c_str();
             std::vector<char> vDatas;
             vDatas.assign(datas, datas + str_size);
             (*this)(vDatas);
        }
        /**
        *\fn void operator(T* data, D...)
        *\brief The pointer to the std::string to write.
        *\param T* data : the pointer to the data to write.
        *\param D... : used for SFINAE.
        */
        template <typename T,
              class... D,
              class = typename std::enable_if<std::is_fundamental<T>::value>::type,
              class = typename std::enable_if<std::is_same<T, std::string>::value>::type,
              class = typename std::enable_if<!std::is_enum<T>::value>::type>
        void operator() (T* data, D...) {
            if (data != nullptr) {
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(reinterpret_cast<unsigned long long int>(data));
                if (it != adresses.end()) {
                    buffer<<it->second<<std::endl;
                } else {
                    std::pair<unsigned long long int, unsigned long long int> newAddress (reinterpret_cast<unsigned long long int>(data), nbSerialized);
                    adresses.insert(newAddress);
                    buffer<<newAddress.second<<std::endl;
                    (*this)(*data);
                    nbSerialized++;
                }
            } else {
                int id = -1;
                buffer<<id<<std::endl;
            }
        }
        //Static objects.
        /**
        *\fn void operator(O& data, D...)
        *\brief register a static object onto the archive.
        *\param O& the object to register.
        *\param D... : used for SFINAE.
        */
        template <class O,
                  class... D,
                  class = typename std::enable_if<!std::is_fundamental<O>::value>::type,
                  class = typename std::enable_if<!std::is_same<O, std::string>::value && !std::is_pointer<O>::value>::type,
                  class = typename std::enable_if<!has_typedef_key<O>::value>::type,
                  class = typename std::enable_if<!std::is_enum<O>::value>::type>
        void operator() (O& object, D...) {
            object.serialize(*this);
        }
        template <class O,
                  class... D,
                  class = typename std::enable_if<!std::is_fundamental<O>::value>::type,
                  class = typename std::enable_if<!std::is_same<O, std::string>::value>::type,
                  class = typename std::enable_if<!has_typedef_key<O>::value>::type,
                  class = typename std::enable_if<!std::is_enum<O>::value>::type>
        /**
        *\fn void operator(O* data, D...)
        *\brief register a static object onto the archive.
        *\param O* the pointer to the object to register.
        *\param D... : used for SFINAE.
        */
        void operator() (O* object, D...) {
            if (object != nullptr) {
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(reinterpret_cast<unsigned long long int>(object));
                if (it != adresses.end()) {
                    buffer<<it->second<<" ";
                } else {
                    std::pair<unsigned long long int, unsigned long long int> newAddress (reinterpret_cast<unsigned long long int>(object), nbSerialized);
                    adresses.insert(newAddress);
                    buffer<<newAddress.second<<" ";
                    object->serialize(*this);
                    nbSerialized++;
                }
            } else {
                int id = -1;
                buffer<<id<<std::endl;
            }
        }
        //Dynamic objects.
        /**
        *\fn void operator(O& data, D...)
        *\brief register a dynamic object onto the archive.
        *\param O& the object to register.
        *\param D... : used for SFINAE.
        */
        template <class O,
                  class... D,
                  class = typename std::enable_if<!std::is_fundamental<O>::value>::type,
                  class = typename std::enable_if<!std::is_same<O, std::string>::value>::type,
                  class = typename std::enable_if<has_typedef_key<O>::value>::type,
                  class = typename std::enable_if<!std::is_enum<O>::value>::type,
                  class = typename std::enable_if<!sizeof...(D)>::type>
        void operator() (O& object, D...) {
            if (typeid(decltype(object)).name() == typeid(object).name()) {
                object.vtserialize(*this);
            } else {
                object.key.register_object(&object);
                object.key.serialize_object("serialize", "OTextArchive", *this);
            }
        }
        /**
        *\fn void operator(O& data, D...)
        *\brief register pointer to a dynamic object onto the archive.
        *\param O& the object to register.
        *\param D... : used for SFINAE.
        */
        template <class O,
                  class... D,
                  class = typename std::enable_if<!std::is_fundamental<O>::value>::type,
                  class = typename std::enable_if<!std::is_same<O, std::string>::value>::type,
                  class = typename std::enable_if<has_typedef_key<O>::value>::type,
                  class = typename std::enable_if<!std::is_enum<O>::value>::type,
                  class = typename std::enable_if<!sizeof...(D)>::type>
        void operator() (O* object, D...) {
            if (object != nullptr) {
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(reinterpret_cast<unsigned long long int>(object));
                if (it != adresses.end()) {
                    buffer<<it->second<<std::endl;
                } else {
                    std::string typeName = "BaseType";
                    std::pair<unsigned long long int, unsigned long long int> newAddress (reinterpret_cast<unsigned long long int>(object), nbSerialized);
                    adresses.insert(newAddress);
                    if (typeid(decltype(*object)).name() == typeid(*object).name()) {
                        buffer<<newAddress.second<<std::endl;
                        buffer<<typeName<<std::endl;
                        object->vtserialize(*this);
                    } else {
                        object->key.register_object(object);
                        typeName = object->key.getTypeName();
                        buffer<<newAddress.second<<std::endl;
                        buffer<<typeName<<std::endl;
                        object->key.serialize_object("serialize", "OTextArchive", *this);
                    }
                    nbSerialized++;
                }
            } else {
                int id = -1;
                buffer<<id<<std::endl;
            }
        }
        //std::vectors.
        /**
        *\fn void operator(O& data, D...)
        *\brief register a list of objects onto the archive.
        *\param std::vector<O>& the list of objects to register.
        *\param D... : used for SFINAE.
        */
        template <class O>
        void operator() (std::vector<O>& data) {
            std::size_t size = data.size();
            buffer<<size<<std::endl;
            for (unsigned int i = 0; i < data.size(); i++)
                 (*this)(data[i]);
        }
        template <class T>
        void operator() (SharedPointer<T>& ptr) {
            (*this)(ptr.get());
        }
        template <class T>
        void operator() (UniquePointer<T>& ptr) {
            (*this)(ptr.get());
        }
        /**
        * Simple hash function. Takes two utf16 characters and enumerate
        * their hash value. The value is used as dictionary array index.
        *
        * @param first - utf16 character
        * @param second - utf16 character
        * @return hash value in interval from 0 to DICT_LEN
        */
        u16 hash(u16 char0, u16 char1)
        {
          return ((char1 * 37 ^ ((char0 >> 7) * 5) ^ (char0)) * 33) & DICT_LEN;
        }
     
        /**
        * Initialize the compressor. It is called from utf16_compress only.
        */
        void utf16_compressor_init(void)
        {
          if (utf16_compressor_initialized)
           return;
          utf16_compressor_initialized = 1;
          /* clear the hash table. It needs to be called only once to prevent access
          unaligned pointer which causes crash on some architectures. */
          memset(dictionary, 0, sizeof(dictionary));
        }
        /**
        * Writes one output byte.
        *
        * @param out - output byte
        * @param output - pointer to output array.
        */
        void OutputByte(u8 out, u8 **output)
        {
          **output = out;
          *output += 1;
        }
        /**
        * Encode pair of length and offset values. OutpurByte function is called on each output byte.
        *
        * @param offset - offset
        * @param len - length
        * @param pointer to output array
        */
        void OutputMatch(u16 offset, u16 len, u8 **output)
        {
          OutputByte((u8)(len & 0x3F) | ((len > 0x3F) << 6) | 0x80, output);
          len >>= 6;
     
          while (len > 0) {
            OutputByte((u8)(len & 0x7F) | ((len > 0x7F) << 7), output);
            len >>= 7;
          }
     
          OutputByte((u8)(offset & 0x3F) | ((offset > 0x3F) << 6) | 0x80, output);
          offset >>= 6;
     
          while (offset > 0) {
            OutputByte((u8)(offset & 0x7F) | ((offset > 0x7F) << 7), output);
            offset >>= 7;
          }
        }
     
        /**
        * Encode array of literals. OutpurByte function is called on each output byte.
        *
        * @param input - array of utf16 literals
        * @param len - number of input utf16 literals
        * @param output - pointer to output byte array.
        */
     
        void OutputLiteral(u16 *input, u16 len, u8 **output)
        {
          u16 previous, l = len;
          int diff;
     
          /* most significant bit is 0 to mark a literal */
          OutputByte((u8)(len & 0x3F) | ((len > 0x3F) << 6), output);
     
          len >>= 6;
     
          while (len > 0) {
            OutputByte((u8)(len & 0x7F) | ((len > 0x7F) << 7), output);
            len >>= 7;
          }
     
          /* save the first Unicode character */
          previous = *input++;
          OutputByte(previous & 0xFF, output);
          OutputByte(previous >> 8, output);
     
          /* save differences between the characters */
          len = l - 1;
          while (len-- > 0) {
            diff = previous - *input;
            if ((diff & 0xFFFFFFC0) == 0 || (diff | 0x3F) == -1)
              OutputByte((u8)(diff & 0x7F), output);
            else {
              OutputByte((u8)((diff & 0x7F) | 0x80), output);
              diff >>= 7;
              if ((diff & 0xFFFFFFC0) == 0 || (diff | 0x3F) == -1)
                OutputByte((u8)(diff & 0x7F), output);
              else {
                OutputByte((u8)((diff & 0x7F) | 0x80), output);
                OutputByte(diff >> 7, output);
              }
            }
            previous = *input++;
          }
        }
     
        /**
        * utf16 Compress function. This function implements described compression algorithm.
        *
        * @param input - array of input utf16 characters
        * @param length - number of input utf16 characters
        * @param output - output buffer
        * @return number of output bytes
        */
        int utf16_compress(u16 *input, int length, u8 *output)
        {
          int i;
          u16 *input_orig = input;
          u8 *o = output;
          u16 *literal, *match, match_index;
     
          utf16_compressor_init();
     
          literal = input;
          while (length-- > 0)
          {
            /* check for previous occurrence */
            match_index = hash(*input, *(input + 1));
            match = dictionary[match_index];
            dictionary[match_index] = input;
            if (match >= input_orig && match < input && *match == *input && *(match + 1) == *(input + 1))
            {
              /* Previous occurrence was found. Encode literals...*/
              if (literal < input)
                OutputLiteral(literal, input - literal, &output);
              i = 2;
              /* ...find the occurrence length...*/
              while (*(match + i) == *(input + i))
                ++i;
              /* ...and encode the (offset, length) pair */
              OutputMatch(input - match, i, &output);
              input += i;
              length -= (i - 1);
              literal = input;
            }
            else
              ++input;
          }
          /* if there are some remaining literals, encode them */
          if (literal < input)
            OutputLiteral(literal, input - literal, &output);
     
          return output - o;
        }
        /** \fn ostream& operator<<(std::ostream& out)
        *   \brief compress the archive's data and put it into an output stream.
        *   \param std::ostream& out : the output stream where to compress the data.
        *   \return std::ostream& : the output stream where the datas are compressed.
        */
        friend std::ostream& operator<<(std::ostream& out, OTextArchive& oa) {
            oa.compress_stdin();
            out<<oa.buffer;
            return out;
        }
    private :
        std::ostream& buffer; /**< the output buffer containing the datas.*/
        int utf16_compressor_initialized = 0; /**<A boolean to test if the compressor is initialized*/
        u16 * dictionary[DICT_LEN]; /**<A the dictionnary used for the compression algorithm*/
        std::map<unsigned long long int, unsigned long long int> adresses; /**< an std::map used to store the adresses and the id of the serialized pointers.*/
        unsigned long long int nbSerialized; /** <the number data which are serialized into the archive*/
    };
    /**
    * \file archive.h
    * \class ITextArchive
    * \brief Read everything from the input archive's buffer.
    * \author Duroisin.L
    * \version 1.0
    * \date 1/02/2014
    */
    class ITextArchive {
    public :
        /**
        *\fn ITextArchive(std::istream& buffer)
        *\brief pass the input stream to the input test archive.
        *\param std::istream& buffer : the input buffer where to read the datas.
        */
        ITextArchive (std::istream& buffer) : buffer(buffer) {
            nbDeserialized = 0;
        }
        /**
        * reads compressed utf16 from stdin, decompress it and writes output to stdout
        */
        void decompress_stdin(void)
        {
          std::ostringstream output;
          output<<buffer;
          int i, out_len;
          char *buffer, *output_buffer;
          i = output.str().size();
     
          /* allocate input and output buffers */
          buffer = new char[MAX_COMPRESSED_LENGTH];
          output_buffer = new char[MAX_DECOMPRESSED_LENGTH];
     
          /* read input from stdin */
          if (i == MAX_DECOMPRESSED_LENGTH)
          {
              std::cerr<<"ERROR: Input is too long!"<<std::endl;
              return;
          }
          buffer = const_cast<char*> (output.str().c_str());
          output.str("");
          /* make decompression */
          out_len = utf16_decompress((u8 *)buffer, i, (u16 *)output_buffer);
          //delete[] buffer;
          this->buffer.rdbuf()->pubsetbuf(output_buffer, out_len);
          //delete[] output_buffer;
        }
        //Fundamentals.
        /**
        * \fn void operator(T& data, D...)
        * \brief read a fundamental type from the archive.
        * \param T& the data to read.
        * \param D... used for SFINAE.
        */
        template <typename T,
              class... D,
              class = typename std::enable_if<std::is_fundamental<T>::value>::type>
        void operator() (T& data, D...) {
             buffer>>data;
             char space;
             buffer.get(space);
        }
        /**
        * \fn void operator(T& data, D...)
        * \brief read a char from the archive. (we need to read unformatted input here to also read special chars like \n, spaces, etc...)
        * \param T& the data to read.
        * \param D... used for SFINAE.
        */
        void operator() (char& data) {
             buffer.get(data);
             char space;
             buffer.get(space);
        }
        /**
        * \fn void operator(T& data, D...)
        * \brief read an unsigned char from the archive. (we need to read unformatted input here to also read special chars like \n, spaces, etc...)
        * \param T& the data to read.
        * \param D... used for SFINAE.
        */
        void operator() (unsigned char& data) {
             buffer.get((char&) data);
             char space;
             buffer.get(space);
        }
     
        /**
        * \fn void operator(T& data, D...)
        * \brief read a pointer to a fundamental type from the archive.
        * \param T& the data to read.
        * \param D... used for SFINAE.
        */
        template <typename T,
              class... D,
              class = typename std::enable_if<std::is_fundamental<T>::value>::type>
        void operator() (T*& data, D...) {
            unsigned long long int id;
            buffer>>id;
            char space;
            buffer.get(space);
            if (id != -1) {
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(id);
                if (it != adresses.end()) {
                    data = reinterpret_cast<T*> (it->second);
                } else {
                    data = new T();
                    (*this)(*data);
                    std::pair<unsigned long long int, unsigned long long int> newAddress (id, reinterpret_cast<unsigned long long int>(data));
                    adresses.insert(newAddress);
                    nbDeserialized++;
                }
            }
        }
        template <typename E,
                class... D,
                class = typename std::enable_if<!std::is_fundamental<E>::value>::type,
                class = typename std::enable_if<std::is_enum<E>::value>::type>
        void operator()(E& data, D...) {
            int eVal;
            buffer>>eVal;
            data = static_cast<E>(eVal);
            char space;
            buffer.get(space);
        }
        template <typename E,
                class... D,
                class = typename std::enable_if<!std::is_fundamental<E>::value>::type,
                class = typename std::enable_if<std::is_enum<E>::value>::type>
        void operator() (E*& data, D...) {
            unsigned long long int id;
            buffer>>id;
            char space;
            buffer.get(space);
            if (id != -1) {
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(id);
                if (it != adresses.end()) {
                    data = reinterpret_cast<E*> (it->second);
                } else {
                    data = new E();
                    (*this)(*data);
                    std::pair<unsigned long long int, unsigned long long int> newAddress (id, reinterpret_cast<unsigned long long int>(data));
                    adresses.insert(newAddress);
                    nbDeserialized++;
                }
            }
        }
        //std::string.
        /**
        * \fn void operator(T& data, D...)
        * \brief read an std::string from the archive.
        * \param T& the data to read.
        * \param D... used for SFINAE.
        */
        template <typename T,
              class... D,
              class = typename std::enable_if<!std::is_fundamental<T>::value>::type,
              class = typename std::enable_if<std::is_same<T, std::string>::value>::type,
              class = typename std::enable_if<!std::is_enum<T>::value>::type>
        void operator() (T& data, D...) {
            std::vector<char> vDatas;
            (*this)(vDatas);
            const char* datas = &vDatas[0];
            std::size_t str_size = vDatas.size();
            data = std::string(datas, str_size);
        }
        /**
        * \fn void operator(T& data, D...)
        * \brief read a pointer to an std::string from the archive.
        * \param T* the data to read.
        * \param D... used for SFINAE.
        */
        template <typename T,
              class... D,
              class = typename std::enable_if<!std::is_fundamental<T>::value>::type,
              class = typename std::enable_if<std::is_same<T, std::string>::value>::type,
              class = typename std::enable_if<!std::is_enum<T>::value>::type>
        void operator() (T*& data, D...) {
            unsigned long long int id;
            buffer>>id;
            char space;
            buffer.get(space);
            if (id != -1) {
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(id);
                if (it != adresses.end()) {
                    data = reinterpret_cast<T*> (it->second);
                } else {
                    data = new T();
                    (*this)(*data);
                    std::pair<unsigned long long int, unsigned long long int> newAddress (id, reinterpret_cast<unsigned long long int>(data));
                    adresses.insert(newAddress);
                    nbDeserialized++;
                }
            }
        }
        //Static objects.
        /**
        * \fn void operator(O& data, D...)
        * \brief read a static object from the archive.
        * \param O& the data to read.
        * \param D... used for SFINAE.
        */
        template <class O,
                  class... D,
                  class = typename std::enable_if<!std::is_fundamental<O>::value>::type,
                  class = typename std::enable_if<!std::is_same<O, std::string>::value && !std::is_pointer<O>::value>::type,
                  class = typename std::enable_if<!has_typedef_key<O>::value>::type,
                  class = typename std::enable_if<!std::is_enum<O>::value>::type>
        void operator() (O& object, D...) {
            object.serialize(*this);
        }
        /**
        * \fn void operator(O& data, D...)
        * \brief read a pointer to a static object from the archive.
        * \param O* the data to read.
        * \param D... used for SFINAE.
        */
        template <class O,
                  class... D,
                  class = typename std::enable_if<!std::is_fundamental<O>::value>::type,
                  class = typename std::enable_if<!std::is_same<O, std::string>::value>::type,
                  class = typename std::enable_if<!has_typedef_key<O>::value>::type,
                  class = typename std::enable_if<!std::is_enum<O>::value>::type>
        void operator() (O*& object, D...) {
            unsigned long long int id;
            buffer>>id;
            char space;
            buffer.get(space);
            if (id != -1) {
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(id);
                if (it != adresses.end()) {
                    object = reinterpret_cast<O*>(it->second);
                } else {
                    object = new O();
                    object->serialize(*this);
                    std::pair<unsigned long long int, unsigned long long int> newAddress (id, reinterpret_cast<unsigned long long int>(object));
                    adresses.insert(newAddress);
                    nbDeserialized++;
                }
            }
        }
        //Dynamic objects.
        /**
        * \fn void operator(O* data, D...)
        * \brief read a dynamic object from the archive.
        * \param O& the data to read.
        * \param D... used for SFINAE.
        */
        template <class O,
                  class... D,
                  class = typename std::enable_if<!std::is_fundamental<O>::value>::type,
                  class = typename std::enable_if<!std::is_same<O, std::string>::value>::type,
                  class = typename std::enable_if<has_typedef_key<O>::value>::type,
                  class = typename std::enable_if<!std::is_enum<O>::value>::type,
                  class = typename std::enable_if<!sizeof...(D)>::type>
        void operator() (O& object, D...) {
            if (typeid(decltype(object)) == typeid(object)) {
                object.vtserialize(*this);
            } else {
                object.key.register_object(&object);
                object.key.serialize_object("serialize", "ITextArchive", *this);
            }
        }
        /**
        * \fn void operator(O* data, D...)
        * \brief read a pointer to a non abstract dynamic object from the archive.
        * \param O* the data to read.
        * \param D... used for SFINAE.
        */
        template <class O,
                  class... D,
                  class = typename std::enable_if<!std::is_fundamental<O>::value>::type,
                  class = typename std::enable_if<!std::is_same<O, std::string>::value>::type,
                  class = typename std::enable_if<!std::is_enum<O>::value>::type,
                  class = typename std::enable_if<has_typedef_key<O>::value && !std::is_abstract<O>::value>::type,
                  class = typename std::enable_if<!sizeof...(D)>::type>
        void operator() (O*& object, D...) {
            unsigned long long int id;
            std::string typeName;
            buffer>>id;
            char space;
            buffer.get(space);
            if (id != -1) {
                getline(buffer, typeName);
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(id);
                if (it != adresses.end()) {
                    object = reinterpret_cast<O*>(it->second);
                } else {
                    if (typeName == "BaseType") {
                         object = new O();
                         object->vtserialize(*this);
                        std::pair<unsigned long long int, unsigned long long int> newAddress (id, reinterpret_cast<unsigned long long int>(object));
                        adresses.insert(newAddress);
                    } else {
                        object = O::allocate(typeName);
                        object->key.register_object(object);
                        object->key.serialize_object("serialize", "ITextArchive", *this);
                        std::pair<unsigned long long int, unsigned long long int> newAddress (id, reinterpret_cast<unsigned long long int>(object));
                        adresses.insert(newAddress);
                    }
                    nbDeserialized++;
                }
            }
        }
        /**
        * \fn void operator(O* data, D...)
        * \brief read a pointer to an abstract dynamic object from the archive.
        * \param O* the data to read.
        * \param D... used for SFINAE.
        */
        template <class O,
                  class... D,
                  class = typename std::enable_if<!std::is_fundamental<O>::value>::type,
                  class = typename std::enable_if<!std::is_same<O, std::string>::value>::type,
                  class = typename std::enable_if<!std::is_enum<O>::value>::type,
                  class = typename std::enable_if<has_typedef_key<O>::value>::type,
                  class = typename std::enable_if<std::is_abstract<O>::value>::type,
                  class = typename std::enable_if<!sizeof...(D)>::type>
        void operator() (O*& object, D...) {
            std::string typeName;
            unsigned long long int id;
            buffer>>id;
            if (id != -1) {
                char space;
                buffer.get(space);
                getline(buffer, typeName);
                std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(id);
                if (it != adresses.end()) {
                    object = reinterpret_cast<O*>(it->second);
                } else {
                    object = O::allocate(typeName);
                    object->key.register_object(object);
                    object->key.serialize_object("serialize", "ITextArchive", *this);
                    std::pair<unsigned long long int, unsigned long long int> newAddress (id, reinterpret_cast<unsigned long long int>(object));
                    adresses.insert(newAddress);
                    nbDeserialized++;
                }
            }
        }
        /**
        * \fn void operator(O* data, D...)
        * \brief read a list of objects from the archive.
        * \param O* the data to read.
        * \param D... used for SFINAE.
        */
        template <class O>
        void operator() (std::vector<O>& objects) {
            std::size_t size;
            buffer>>size;
            char space;
            buffer.get(space);
            for (unsigned int i = 0; i < size; i++) {
                O object;
                (*this)(object);
                objects.push_back(object);
            }
        }
        template <class T>
        void operator() (SharedPointer<T>& ptr) {
            (*this)(ptr.get());
        }
        template <class T>
        void operator() (UniquePointer<T>& ptr) {
            (*this)(ptr.get());
        }
        /**
        * Decode length value. The offset is decoded the same way.
        *
        * @param input - pointer to array of bytes which encodes the length.
        * @return the encoded length/offset value. *input pointer is shifted by the read length.
        */
        int DecodeLength(u8 **input)
        {
          int ret = 0;
          int shift = 6;
     
          u8 *b = *input;
     
          (*input)++;
          ret += *b & 0x3f;
     
          if ((*b & 0x40) == 0)
            return ret;
     
          do
          {
            b = *input;
            (*input)++;
            ret |= ((int)(*b & 0x7f)) << shift;
            shift+=7;
          } while ((*b & 0x80) != 0);
     
          return ret;
        }
     
        /**
        * Writes one output byte.
        *
        * @param out - output byte
        * @param output - pointer to output array.
        */
        void OutputByte(u8 out, u8 **output)
        {
          **output = out;
          *output += 1;
        }
         /**
        * utf16 Decompress function. This function implements described decompression algorithm.
        *
        * @param input - input compressed data
        * @param len - length of input
        * @param output - output buffer
        * @return number of extracted utf16 characters
        */
        int utf16_decompress(u8 *input, int len, u16 *output)
        {
          u8 *o = (u8 *)output;
          int offset, length;
          u8 *end = input + len;
          int c;
     
          while (input < end) {
          /* decite what to decode. Match or set of literals?*/
            if (*input & 0x80)
            {  /* match */
              length = DecodeLength(&input);
              offset = DecodeOffset(&input);  // same algorithm as DecodeLength
              while (length-- > 0)
              {
                *output = *(output - offset);
                ++output;
              }
            }
            else
            {
              /* literal */
              length = DecodeLength(&input);
              *output = *input++;
              *output++ |= ((unsigned int)(*input++)) << 8;
              --length;
     
              while (length-- > 0) {
                c = *input & 0x7F;
     
                if (*input++ & 0x80) {    /* two bytes */
                  c |= ((unsigned int)*input & 0x7F) << 7;
     
                  if (*input++ & 0x80) {  /* three bytes */
                    c |= *input++ << 14;
     
                    if (c & 0x10000)      /* negative number */
                      c |= 0xFFFF0000;
                  }
                  else if (c & 0x2000)    /* negative number */
                    c |= 0xFFFFC000;
                }
                else if (c & 0x40)        /* negative number */
                  c |= 0xFFFFFF80;
     
                *output = *(output - 1) - c;
                ++output;
              }
            }
          }
          return (u8 *) output - o;
        }
        /** \fn istream& operator<<(std::istream& in)
        *   \brief decompress the archive's data and put it into an input stream.
        *   \param std::istream& in : the input stream where to decompress the data.
        *   \return the input stream where the datas are decompressed.
        */
        friend std::istream& operator>>(std::istream& in, ITextArchive& ia) {
            std::streambuf * pbuf = in.rdbuf();
            long size = pbuf->pubseekoff(0,in.end);
            char* contents = new char [size];
            pbuf->sgetn (contents,size);
            ia.buffer.rdbuf()->pubsetbuf(contents, size);
            ia.decompress_stdin();
            return in;
        }
    private :
        std::istream& buffer; /**< the buffer where to read the data.*/
        std::map<unsigned long long int, unsigned long long int> adresses; /**< an std::map used to store ids and adresses of readed pointers.*/
        unsigned long long int nbDeserialized; /** the nb object which have been deserailized.*/
    };
    }
    #endif // ODFAEG_ARCHIVE

    De plus std::unique_ptr force la récupération d'un pointeur nu pour un propriétaire temporaire, imagine ce qui se passerait si j'essaye d'utiliser un pointeur nu d'un std::unique_ptr qui a été delete juste avant.

    Je préfère avoir une classe du genre WeakPointer, qui lance une exception dans se cas là, surtout si le pointeur nu est dans un contenaire, ce qui est plus difficile à déboguer.

  2. #22
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Tu n'as pas l'impression que de devoir systématiquement tout refaire est peut-être un signe qu'il faut te remettre en question à la place de considérer que c'est systématiquement les autres qui ont mal fait les choses ?

  3. #23
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Lolilolight Voir le message
    Non, je ne pense pas que ça soit en dehors des contraintes car, j'ai besoin de modifier l'adresse du pointeur possédé par mon std::unique_ptr via une fonction sans type de retour qui prend une référence sur le pointeur possédé par mon std::unique_ptr (cette fonction modifie aussi l'adresse des pointeurs nu lors de la lecture de données)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void f(std::unique_ptr<T> &p, T*newPointedObject)
    {
      p.reset(newPointedObject);
    }
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  4. #24
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Et si tu ne peux modifier l'interface de ta fonction, tu devrais toujours pouvoir lui passer un objet proxy faisant ça.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  5. #25
    Invité
    Invité(e)
    Par défaut
    Bon, ça compile, j'ai juste du rajouté un std::move ici.

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    template <class O>
        void operator() (std::vector<O>& objects) {
            std::size_t size;
            buffer>>size;
            char space;
            buffer.get(space);
            for (unsigned int i = 0; i < size; i++) {
                O object;
                (*this)(object);
                objects.push_back(std::move(object));
            }
        }

    Car il est vrai que ici le std::unique_ptr n'est pas copiable dans le cas d'un std::unique_ptr.

    Par contre j'ai le sentiment que la classe std::shared_pointer (en ayant regarder son code source) semble assez lourde de plus le compteur n'est pas un pointeur donc, si un thread delete le compteur d'un std::shared_ptr je ne suis pas sûr que celui-ci soit remis à jour automatiquement pour tout les autres std::shared_ptr, de plus, si j'appelle e constructeur de copie sur un std::shared_ptr qui n'est pas nul, j'ai rien vu qui "reset" le compteur, j'ai donc fait un truc comme ceci :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
     
    emplate <typename R, typename Deleter = DefaultDeleter<R>>
    class SharedPointer {
    public :
        SharedPointer() :
        rm (ResourceManager<R>::get()),
        localisation("0")
        {
            counter = new unsigned int(1);
            owner = true;
        }
        SharedPointer(R* resource) :
        rm (ResourceManager<R>::get()),
        localisation("0")
        {
            counter = new unsigned int(1);
            owner = true;
            localisation = rm.make_resource(resource, ResourceManagerBase::INTERNAL);
        }
        SharedPointer(const SharedPointer& sp) :
            rm (ResourceManager<R>::get()),
            localisation(sp.localisation) {
            const_cast<SharedPointer&>(sp).inc();
            counter = sp.counter;
            if (localisation != "0")
                const_cast<SharedPointer&>(sp).reset();
            owner = true;
        }
        template <typename D>
        SharedPointer(const SharedPointer<D>& sp) :
            rm (ResourceManager<R>::get()),
            localisation(rm.make_resource(sp.get(), ResourceManagerBase::INTERNAL)) {
            const_cast<SharedPointer<D>&>(sp).inc();
            counter = sp.counter;
            if (localisation != "0")
                const_cast<SharedPointer<D>&>(sp).reset();
            owner = true;
        }
        SharedPointer& operator=(const R* resource) {
            counter = new unsigned int(1);
            owner = true;
            localisation = rm.make_resource(resource, ResourceManagerBase::INTERNAL);
            return *this;
        }
        SharedPointer& operator=(const SharedPointer& sp) {
            localisation = sp.getLocalisation();
            const_cast<SharedPointer&>(sp).inc();
            counter = sp.counter;
            return *this;
        }
        template <typename D>
        SharedPointer& operator=(const SharedPointer<D>& sp) {
            localisation = rm.make_resource(sp.get(), ResourceManagerBase::INTERNAL);
            const_cast<SharedPointer<D>&>(sp).inc();
            counter = sp.counter;
            return *this;
        }
        unsigned int getCounter() {
            return counter;
        }
        R* get() const {
            return const_cast<R*>(rm.getResourceByPath(localisation));
        }
        ResourceManager<R>& getRM() const {
            return rm;
        }
        std::string getLocalisation() const {
            return localisation;
        }
        R& operator*() const {
            return *const_cast<R*>(rm.getResourceByPath(localisation));
        }
        bool operator==(std::nullptr_t) const {
            return localisation == "0";
        }
        bool operator!=(std::nullptr_t) const {
            return !(localisation == "0");
        }
        R* release() {
            owner = false;
            return get();
        }
        ~SharedPointer() {
            dec();
            std::cout<<"counter : "<<*counter<<std::endl;
            if (owner && *counter == 0 && localisation != "0") {
                R* r = const_cast<R*>(rm.getResourceByPath(localisation));
                deleter(r);
                localisation = "0";
                delete counter;
            }
        }
        void reset(R* r) {
            R* old = const_cast<R*>(rm.getResourceByPath(localisation));
            deleter(old);
            localisation = rm.make_resource(r, ResourceManagerBase::INTERNAL);
            reset();
        }
        void reset() {
            std::lock_guard<std::recursive_mutex> locker(rec_mutex);
            counter = new unsigned int(1);
        }
        void inc() {
            std::lock_guard<std::recursive_mutex> locker(rec_mutex);
            (*counter)++;
        }
        void dec() {
            std::lock_guard<std::recursive_mutex> locker(rec_mutex);
            (*counter)--;
        }
        unsigned int* counter;
    private :
        bool owner;
        Deleter deleter;
        ResourceManager<R>& rm;
        std::string localisation;
    };

    Qui semble bien fonctionner dans ce cas particulier ici :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    void f1(odfaeg::SharedPointer<int> sp) {
         sp = odfaeg::make_shared<int>(5);
         std::cout<<*reinterpret_cast<int*>(sp.get())<<std::endl;
    }
    int main (int argv, char* argc[]) {
        odfaeg::SharedPointer<int> sp = odfaeg::make_shared<int>(5);
     
        f1(sp);
     
        std::cout<<*reinterpret_cast<int*>(sp.get())<<std::endl;
        //return 0;
        EXPORT_CLASS_GUID(BoundingVolumeBoundingBox, odfaeg::BoundingVolume, odfaeg::BoundingBox)
        EXPORT_CLASS_GUID(EntityTile, odfaeg::g2d::Entity, odfaeg::g2d::Tile)
        EXPORT_CLASS_GUID(EntityWall, odfaeg::g2d::Entity, odfaeg::g2d::Wall)
        EXPORT_CLASS_GUID(EntityDecor, odfaeg::g2d::Entity, odfaeg::g2d::Decor)
        EXPORT_CLASS_GUID(EntityShadowTile, odfaeg::g2d::Entity, odfaeg::g2d::ShadowTile)
        EXPORT_CLASS_GUID(EntityShadowWall, odfaeg::g2d::Entity, odfaeg::g2d::ShadowWall)
        EXPORT_CLASS_GUID(EntityAnimation, odfaeg::g2d::Entity, odfaeg::g2d::Anim)
        MyAppli app(sf::VideoMode(800, 600, 32), "Test odfaeg");
        return app.exec();
    }

  6. #26
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Tu as raison, shared_ptr existe depuis plus de 10 ans mais ils ont toujours pas pensés à la situation multi-thread ...

    Chez moi le compteur est un __shared_count<_Lp> qui est un type contenant un _Sp_counted_base<_Lp>* type qui contient un _Atomic_word. J'ai bien l'impression que les problèmes que tu cites n'existe pas.

    Dans tout les cas, c'est le comportement indiqué par la norme (ou un de ces derniers brouillons) qu'il faut regarder, à défaut celui indiqué sur un site servant de documentation de référence, pas en allant trifouiller dans le code source (qui, oui, est complexe, mais ils ont beaucoup plus de contrainte que toi, c'est donc normal que la complexité augmente).

    Aller rechercher dans une table d'association avec comme clé des chaines de caractère, on fait difficilement mieux pour tuer la performance.

    Sinon tes opérateurs d'affectation sont pas bon (et dans un cas aussi complexe, copy-and-swap sans hésiter).

  7. #27
    Invité
    Invité(e)
    Par défaut
    Ok je vois je vais réutiliser alors ce qui existe déjà alors, maintenant que j'ai enfin trouvé pourquoi ça ne compilait pas.

    Car au niveau des std::shared_ptr je dois avouer que je sèche totalement au niveau de leur implémentation surtout pour le compteur.

    unique_ptr est un peu plus facile car ce n'est qu'un passage de propriété donc il y a toujours qu'un seul propriétaire qui doit détruire la ressource mais shared_ptr, je sèche totalement, le mieux est sûrement de réutiliser ce qui existe déjà, par contre, dans le cadre d'un singleton (c'est à dire par exemple un seul gestionnaire de ressources pour toutes les ressources d'un même type), je ne vois pas très bien comment faire si je veux libérer les ressources explicitement de manière safe, car je voudrais faire un système ou le propriétaire (qui est le gestionnaire de ressources) détruit toutes les ressources à la fin du programme, avec un singleton et une variable statique mais je voudrais également, que, on puisse encapsuler les différents gestionnaires (les singleton donc) dans une autre classe (qui servirait de cache) et que dans ce cas là ce soit cette autre classes qui se charge de libérer les ressources contenue dans la variable globale.

    (Sachant que le cache pourrait être partagé entre plusieurs threads donc, il me faut aussi quelque chose de similaire à std::shared_ptr.

    Il me faut aussi quelque chose de similaire à std::shared_ptr. (Mais je vois pas bien comment faire cela actuellement)

    PS : pour l'affectation faut que je corrige j'ai un effet un problème, pour la string, je crois que je vais stocker l'id de la ressource plutôt qu'un std::string, ça ira, plus vite.
    Dernière modification par Invité ; 06/10/2014 à 17h35.

  8. #28
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Citation Envoyé par Lolilolight Voir le message
    <snip> par contre, dans le cadre d'un singleton (c'est à dire par exemple un seul gestionnaire de ressources pour toutes les ressources d'un même type), je ne vois pas très bien comment faire si je veux libérer les ressources explicitement de manière safe, car je voudrais faire un système ou le propriétaire (qui est le gestionnaire de ressources) détruit toutes les ressources à la fin du programme, avec un singleton et une variable statique mais je voudrais également, que, on puisse encapsuler les différents gestionnaires (les singleton donc) dans une autre classe (qui servirait de cache) et que dans ce cas là ce soit cette autre classes qui se charge de libérer les ressources contenue dans la variable globale.
    Le mieux serait encore de trouver le moyen de se passer de cet anti-pattern!!!

    Je t'inviterais volontiers à lire ce billet d'emmanuel au sujet du singleton, si je pensais qu'il y ait la moindre chance que tu en tires toi même les conclusions qui s'imposent.
    (Sachant que le cache pourrait être partagé entre plusieurs threads donc, il me faut aussi quelque chose de similaire à std::shared_ptr.

    Il me faut aussi quelque chose de similaire à std::shared_ptr. (Mais je vois pas bien comment faire cela actuellement)

    PS : pour l'affectation faut que je corrige j'ai un effet un problème, pour la string, je crois que je vais stocker l'id de la ressource plutôt qu'un std::string, ça ira, plus vite.
    Ou bien, il faut que tu accepte l'idée de gérer tes threads différemment, et de faire en sorte que tes threads enfant n'aient pas à s'inquiéter de la validité des pointeurs qu'ils manipulent en t'assurant que seuls des pointeurs valides seront disponibles...
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  9. #29
    Invité
    Invité(e)
    Par défaut
    Bof, je dirais plutôt un pattern monostate dans mon cas, car, c'est pas vraiment un singleton, je n'utilise pas de pointeur sur la variable globale (ce qui conduit à l'ani-pattern) mais j'utilise plutôt, une variable simple et je renvoie une référence dessus.

    Sinon, c'est bon, j'ai corrigé pour l'opérateur d'affectation, en fait il y a deux cas, le cas ou les pointeurs des deux pointeurs intelligent pointent sur le même objet, et l'autre cas.

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
     
    template <typename R, typename Deleter = DefaultDeleter<R>>
    class SharedPointer {
    public :
        SharedPointer() :
        rm (ResourceManager<R>::get()),
        localisation("0")
        {
            counter = new unsigned int(1);
            owner = true;
        }
        SharedPointer(R* resource) :
        rm (ResourceManager<R>::get()),
        localisation("0")
        {
            counter = new unsigned int(1);
            owner = true;
            localisation = rm.make_resource(resource, ResourceManagerBase::INTERNAL);
        }
        SharedPointer(const SharedPointer& sp) :
            rm (ResourceManager<R>::get()),
            localisation(sp.localisation) {
            const_cast<SharedPointer&>(sp).inc();
            counter = sp.counter;
            owner = true;
        }
        template <typename D>
        SharedPointer(const SharedPointer<D>& sp) :
            rm (ResourceManager<R>::get()),
            localisation(rm.make_resource(sp.get(), ResourceManagerBase::INTERNAL)) {
            const_cast<SharedPointer<D>&>(sp).inc();
            counter = sp.counter;
            owner = true;
        }
        SharedPointer& operator=(const R* resource) {
            counter = new unsigned int(1);
            owner = true;
            localisation = rm.make_resource(resource, ResourceManagerBase::INTERNAL);
            return *this;
        }
        SharedPointer& operator=(const SharedPointer& sp) {
            if (localisation == sp.getLocalisation()) {
                const_cast<SharedPointer&>(sp).inc();
                counter = sp.counter;
            } else {
                dec();
                localisation = rm.make_resource(sp.get(), ResourceManagerBase::INTERNAL);
                const_cast<SharedPointer&>(sp).inc();
                counter = sp.counter;
            }
            return *this;
        }
        template <typename D>
        SharedPointer& operator=(const SharedPointer<D>& sp) {
            if (localisation == sp.getLocalisation()) {
                const_cast<SharedPointer<D>&>(sp).inc();
                counter = sp.counter;
            } else {
                dec();
                localisation = rm.make_resource(sp.get(), ResourceManagerBase::INTERNAL);
                const_cast<SharedPointer<D>&>(sp).inc();
                counter = sp.counter;
            }
            return *this;
        }
        unsigned int getCounter() {
            return counter;
        }
        R* get() const {
            return const_cast<R*>(rm.getResourceByPath(localisation));
        }
        ResourceManager<R>& getRM() const {
            return rm;
        }
        std::string getLocalisation() const {
            return localisation;
        }
        R& operator*() const {
            return *const_cast<R*>(rm.getResourceByPath(localisation));
        }
        bool operator==(std::nullptr_t) const {
            return localisation == "0";
        }
        bool operator!=(std::nullptr_t) const {
            return !(localisation == "0");
        }
        R* release() {
            owner = false;
            return get();
        }
        ~SharedPointer() {
            dec();
        }
        void reset(R* r) {
            R* old = const_cast<R*>(rm.getResourceByPath(localisation));
            deleter(old);
            localisation = rm.make_resource(r, ResourceManagerBase::INTERNAL);
            reset();
        }
        void reset() {
            std::lock_guard<std::recursive_mutex> locker(rec_mutex);
            counter = new unsigned int(1);
        }
        void inc() {
            std::lock_guard<std::recursive_mutex> locker(rec_mutex);
            (*counter)++;
        }
        void dec() {
            std::lock_guard<std::recursive_mutex> locker(rec_mutex);
            (*counter)--;
            std::cout<<"counter : "<<*counter<<std::endl;
            if (owner && *counter == 0 && localisation != "0") {
                R* r = const_cast<R*>(rm.getResourceByPath(localisation));
                deleter(r);
                delete counter;
                localisation = "0";
            }
        }
        unsigned int* counter;
    private :
        bool owner;
        Deleter deleter;
        ResourceManager<R>& rm;
        std::string localisation;
    };

    Je pense que je vais faire pareil pour le cache, pour optimiser ça je vais essayer de mettre des pointeurs sur des ids, plutôt que des std::string, je pense que ça optimisera pour la recherche avec la std::map.

  10. #30
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Mais même le monostate, c'est moyen/moyen... La meilleure solution pour s'assurer qu'une variable existe mais qu'il n'en existe qu'une seule instance, c'est encore de faire en sorte de la déclarer dans un contexte particulier (au pire, la fonction main, ou une des classes que l'on instancie dans main) et de la rendre non copiable et non affectable, ce qui est grandement facilité en C++11
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #31
    Invité
    Invité(e)
    Par défaut
    Ok, bah c'est ce que je fais là, je fais une classe non copiable qui renvoie une référence sur une variable globale (par contre je ne suis pas fan des fameux =delete, je préfère mettre le constructeur de copie et l'opérateur d'affectation dans la partie private de la classe ainsi je peux quand même encore affecter la variable avec un constructeur. (Avec les =delete je ne peux juste que la déclarer. :/)

    J'ai besoin que cette variable soit globale car, je veux qu'elle soit détruite tout à la fin du programme et non pas à la destruction d'un object que je crée dans le main, j'ai pas envie que le développeur aie à créer un objet dans le main avant de pouvoir utiliser un std::shared_ptr surtout que cet objet doit être partagé entre plusieurs shared_ptr ou unique_ptr qui peuvent delete les ressources de cet objet avant la fin de l'exécution du programme si ils deviennent propriétaire de la ressource.

    Sinon, j'ai fais un pointeur sur un id vers les ressources, l'id n'est autre que la position de la ressource dans un vecteur et je mets à jour tout les id dès que je supprime une ressources et comme j'ai fais des pointeurs sur les id, il se mettent à jour dans tout les pointeurs intelligents qui utilisent le même type de ressource. (j'utilise donc un vecteur, plus une std::map) pour stocker les ressources et j'accède à chaque ressource à partir de sa position dans le vecteur dans les pointeurs intelligent, bref, en général l'optimisation j'y regarde à la fin j'essaye d'abord de faire quelque chose qui fonctionne. xD

    Bref je n'ai pas envie de devoir déclarer un objet dans le main et de devoir le passer à chaque pointeur. (Ca serait assez lourd)
    D'autant plus que la singleton est template donc..., je ne peux pas déclarer un objet précis dans le main vu que je ne connais pas encore son type.

  12. #32
    Invité
    Invité(e)
    Par défaut
    Et voila!

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
     
    template <typename R, typename Deleter = DefaultDeleter<R>>
    class UniquePointer {
        public :
        UniquePointer() :
        rm (ResourceManager<R>::Instance()) {
            owner = true;
            id = nullptr;
        }
        UniquePointer(R* resource) :
        rm (ResourceManager<R>::Instance()) {
            id = const_cast<unsigned int*>(&rm.make_resource(resource, ResourceManagerBase::INTERNAL));
            owner = true;
        }
        UniquePointer (UniquePointer&& sp) : rm(std::forward<ResourceManager<R>&>(ResourceManager<R>::Instance())) {
            sp.release();
            id = std::move(sp.getId());
            owner = true;
        }
        template <typename D>
        UniquePointer(UniquePointer<D>&& sp) : rm(std::forward<ResourceManager<R>&>(ResourceManager<R>::Instance())) {
            sp.release();
            id = const_cast<unsigned int*>(&rm.make_resource(sp.get(), ResourceManagerBase::INTERNAL));
            owner = true;
        }
        UniquePointer& operator=(R* resource) {
            id = const_cast<unsigned int*>(&rm.make_resource(resource, ResourceManagerBase::INTERNAL));
            owner = true;
            return *this;
        }
        UniquePointer& operator=(UniquePointer&& sp) {
            sp.release();
            id = std::move(sp.getId());
            return *this;
        }
        template <typename D>
        UniquePointer& operator=(UniquePointer<D>&& sp) {
            sp.release();
            id = const_cast<int*>(&rm.make_resource(sp.get(), ResourceManagerBase::INTERNAL));
            return *this;
        }
        R& operator*() const {
            return *const_cast<R*>(rm.getResourceById(*id));
        }
        R* get() const {
            return const_cast<R*>(rm.getResourceById(*id));
        }
        ResourceManager<R>& getRM() const {
            return rm;
        }
        unsigned int* getId() const {
            return id;
        }
        unsigned int** getPtrToId() {
            return &id;
        }
        bool operator==(std::nullptr_t) const {
            return id == nullptr;
        }
        bool operator!=(std::nullptr_t) const {
            return !(id == nullptr);
        }
        ~UniquePointer() {
            if (owner && id != nullptr) {
                R* r = const_cast<R*>(rm.getResourceById(*id));
                deleter(r);
                rm.updateIds(*id);
                id = nullptr;
            }
        }
        R* release() {
            owner = false;
            return get();
        }
        private :
        bool owner;
        UniquePointer(const UniquePointer& sp) = delete;
        template <typename D>
        UniquePointer (const UniquePointer<D>& sp) = delete;
        UniquePointer& operator=(const UniquePointer& sp) = delete;
        template <typename D>
        UniquePointer& operator=(const UniquePointer<D>& sp) = delete;
        private :
        Deleter deleter;
        ResourceManager<R>& rm;
        unsigned int* id;
     
    };template <class R>
    class WeakPointer {
    public :
        WeakPointer() :
        rm(ResourceManager<R>::Instance()) {
            id = nullptr;
        }
        WeakPointer(const WeakPointer<R>& sp) :
        rm(ResourceManager<R>::Instance()) {
            id = sp.getPtrToId();
        }
        WeakPointer& operator= (const WeakPointer<R>& sp) {
            id = sp.getPtrToId();
            return *this;
        }
        WeakPointer(const SharedPointer<R>& sp) : rm(ResourceManager<R>::Instance()) {
            id = const_cast<SharedPointer<R>&>(sp).getPtrToId();
        }
        WeakPointer& operator= (const SharedPointer<R>& sp) {
            id = const_cast<SharedPointer<R>&>(sp).getPtrToId();
            return *this;
        }
        WeakPointer(const UniquePointer<R>& sp) : rm(ResourceManager<R>::Instance()) {
            id = const_cast<UniquePointer<R>&>(sp).getPtrToId();
        }
        WeakPointer& operator= (const UniquePointer<R>& sp) {
            id = const_cast<UniquePointer<R>&>(sp).getPtrToId();
            return *this;
        }
        bool operator==(std::nullptr_t) const {
            return (*id == nullptr);
        }
        bool operator!=(std::nullptr_t) const {
            return (*id == nullptr);
        }
        bool expired () {
            return (*id == nullptr);
        }
        R& operator*() const {
            return *const_cast<R*>(rm.getResourceById(**id));
        }
        R* get() const {
            return const_cast<R*>(rm.getResourceById(**id));
        }
        R* operator-> () {
            if (expired())
                throw Erreur(0,"Use of deleted pointer!", 1);
            return const_cast<R*>(rm.getResourceById(**id));
        }
        unsigned int** getPtrToId() const {
            return id;
        }
    private :
        ResourceManager<R>& rm;
        unsigned int** id;
    };

    J'en ai profité pour faire un double pointeur sur l'id dans ma classe WeakPtr qui est un pointeur temporaire, je vais retirée les opérateurs de déréférencement du pointeur dans les classe Unique et Shared ptr d'ailleurs et maintenant ça va bien lancer une exception si j'essaye d'utiliser un pointeur temporaire qui a été delete :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    odfaeg::WeakPointer<int> wk;
        {
            odfaeg::UniquePointer<int> sp = odfaeg::make_unique<int>(5);
            wk = sp;
            std::cout<<*reinterpret_cast<int*>(wk.get())<<std::endl;
     
        }
        //Le pointeur a été delete par le std::unique_ptr, lance une exception si on essaye d'accéder au pointeur temporaire.
        std::cout<<wk.expired()<<std::endl;
        return 0;

    Bref, je passe en résolu, faire ses propres classe de pointeurs intelligent n'est pas si mal finalement.

  13. #33
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Ok, bah c'est ce que je fais là, je fais une classe non copiable qui renvoie une référence sur une variable globale (par contre je ne suis pas fan des fameux =delete, je préfère mettre le constructeur de copie et l'opérateur d'affectation dans la partie private de la classe ainsi je peux quand même encore affecter la variable avec un constructeur. (Avec les =delete je ne peux juste que la déclarer. :/)
    Où diable as-tu été chercher ca
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    class NonCopyable{
        public:
            NonCopyable():str_("hello");
            NonCopyable(std::string const & str):str_(str){}
            NonCopyable(NonCopyable const &) = delete;
            NonCopyable & operator = (NonCopyable const &) = delete;
            virtual ~NonCopyable(){}
            void print(std::ostream & os) const{os << str_<<"\n";}
        private :
            std::string str_;
    };
    int main(){
        NonCopyable nc("salut");
        nc.print();
        // NonCopyable copy = nc; !!!! refusé à la compilation opérateur d'affectation et constructeur de copie déclarés delete
        NonCopyable hello;
        hello.print();
        return 0;
    }
    est un code tout à fait valide en C++11, qui compilera parfaitement et qui réagira exactement de la manière dont en est en droit de s'y attendre.

    Ce que l'on déclare delete, ce sont uniquement les mécanismes qui permettent la copie et l'affectation ( AKA constructeur de copie et opérateur d'affectation), cela ne change absolument rien en ce qui concerne la construction qui n'a pas pour but de créer une copie

    La déclaration (sans implémentation) du constructeur de copie et de l'opérateur d'affectation dans l'accessibilité privé n'est qu'un pis aller pour garantir les deux caractéristiques primordiales d'une classe ayant sémantique d'entité car le compilateur ne réagira pas à une tentative de copie ou d'affectation dans une des fonctions membres de la classe, reportant le diagnostique du problème jusqu'à l'édition des liens (qui échouera au motif d'un symbole non défini), au prix de la grosse perte de temps que peut représenter un processus de compilation (presque) complet sur des projets importants...

    J'ai besoin que cette variable soit globale car, je veux qu'elle soit détruite tout à la fin du programme et non pas à la destruction d'un object que je crée dans le main, j'ai pas envie que le développeur aie à créer un objet dans le main avant de pouvoir utiliser un std::shared_ptr surtout que cet objet doit être partagé entre plusieurs shared_ptr ou unique_ptr qui peuvent delete les ressources de cet objet avant la fin de l'exécution du programme si ils deviennent propriétaire de la ressource.
    Typiquement, la fonction main doit être composée de trois étapes:
    1. initialisation de départ
    2. exécution
    3. finalisation et libération des ressources restantes.

    Ce qu'il y a de bien avec le paradigme OO, c'est que le (1) peut être pris en charge par le constructeur d'une classe (Application ), que le (2) peut être pris en charge par une des fonctions membres de cette classe (run ) et que le (3) peut être pris en charge par le destructeur de cette classe (~Application ). Au final, ta fonction main devrait pouvoir se limiter à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int main(){
        Application app(/* ... paramètres éventuels */ );
        app.run(); // ou app.execute, si tu préfères
        return 0; // appel automatique de ~Application pour libérer les dernière ressources
    }
    Si ta classe dispose d'un membre (RessourceHolder ressources_ )qui apparait en premier dans l'ordre de déclaration des variables membres, tu as la certitude de par la norme que ce sera le dernier objet détruit juste avant de quitter l'application. Et, si tu as pris soin de rendre cet objet non copiable et non affectable, tu auras la certitude
    Sinon, j'ai fais un pointeur sur un id vers les ressources, l'id n'est autre que la position de la ressource dans un vecteur et je mets à jour tout les id dès que je supprime une ressources et comme j'ai fais des pointeurs sur les id, il se mettent à jour dans tout les pointeurs intelligents qui utilisent le même type de ressource. (j'utilise donc un vecteur, plus une std::map) pour stocker les ressources et j'accède à chaque ressource à partir de sa position dans le vecteur dans les pointeurs intelligent, bref, en général l'optimisation j'y regarde à la fin j'essaye d'abord de faire quelque chose qui fonctionne. xD
    Heuu... Te rend tu compte qu'un pointeur (qui plus est un shared_pointer!!) sur une id fait que la mécanique interne de ce pointeur est plus lourde que la valeur qu'il doit représenter

    Je suis le premier à plaider en faveur de l'utilisation d'id plutôt que de pointeurs de manière générale, mais l'idée est bel et bien d'utiliser des ids, non des pointeurs sur ces ids

    Bref je n'ai pas envie de devoir déclarer un objet dans le main et de devoir le passer à chaque pointeur. (Ca serait assez lourd)
    Si ton code est correctement organisé (entre autres, si tu utilise une fabrique pour la création de tes objets polymorphes), le passage de cet objet est le cadet de tes soucis : tu le donnes à la fabrique qui se charge de le fournir à tous les objets créés, quitte à ce que ce soit un pointeur et non une référence (bien que je n'aime pas trop cette idée) déclarée dans la classe de base (dans une accessibilité à définir) et au pire, à déclarer la fabrique amie de la classe parent (ancêtre) pour lui permettre de mettre le pointeur à jour avant le renvoi définitif du pointeur sur l'objet créé
    D'autant plus que la singleton est template donc..., je ne peux pas déclarer un objet précis dans le main vu que je ne connais pas encore son type.
    Il y a bien un moment où les template finissent par être instanciées, d'autant plus que la version template du patron singleton utilise généralement le CRTP sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class MySpecificHolder : public Singleton<MySpecificHolder>{
        /* ... */
    };
    Enfin, il n'y a pas grand chose de plus facile que de se passer d'un singleton... Il faut juste garder les principes SOLID en tête et accepter de revoir un peu sa manière de penser
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  14. #34
    Invité
    Invité(e)
    Par défaut
    Ok mais quel gestionnaire de ressources utiliser dans la classe Application, si ils peuvent être de plusieurs type différent ?

    J'ai fais une classe ResourceCache qui permet d'ajouter et de récupérer, n'importe quel type de gestionnaire de ressource via un type commun, avant je la déclarais dans la classe application, ainsi, je n'avais plus besoin de faire de singleton, mais ensuite je suis vite repasser par un singleton car je trouvais pas cela pas très pratique, de plus, le cache n'est pas la seule classe ayant besoin d'avoir accès aux ressources, mais tout les autres pointeurs intelligent ont aussi besoin d'y accéder donc, il fallait que je fasse une variable globale pour chaque type de gestionnaire de ressources, pas le choix, car le type du paramètre template de la ressource n'est défini dans la classe application, c'est au développeur lui même à le définir lors de la création des caches qui se partagent toutes les ressources de l'application. (C'est à dire tout les pointeurs)
    Je vois mal comment faire cela, à part en faisant une méthode statique qui retourne le gestionnaire de ressource unique lors de la définition du type de la ressource par le développeur à la création d'un type de cache. (UniquePointer, SharedPointer, etc...)


    Pour les id (et non des pointeurs sur les id) se serait lourd car il faudrait remettre à jour les ids manuellement dans tout les caches à chaque fois que j'erase une ressource dans le vecteur du gestionnaire de ressources. Je ne crois pas que ça serait très performant de devoir remettre à jour tout cela manuellement hors que avec les pointeurs tu changes la valeur à un endroit et elle est automatiquement changée à tout les autres endroits du programme.

    De plus, le gestionnaire de ressource ne connais pas tout les caches qui utilisent la ressources, donc, je n'ai pas le choix là non plus, il faut que j'utilise un pointeur sur l'id.

  15. #35
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Lolilolight Voir le message
    Ok mais quel gestionnaire de ressources utiliser dans la classe Application, si ils peuvent être de plusieurs type différent ?
    Ceux dont tu as besoin et qui doivent être disponible au niveau du contexte le plus général que tu puisse trouver : celui de l'application.

    Normalement, cela ne devrait pas en faire plus d'un ou deux (les ressources propres au business uniquement)... Les autres ressources (images, sons, fichiers, whatever,...) sont des ressources dont tu n'as, typiquement, besoin que dans certains contextes à ce point spécifiques que tu serais bien inspiré d'en faire carrément des modules indépendants, avec tout ce que cela peut impliquer
    J'ai fais une classe ResourceCache qui permet d'ajouter et de récupérer, n'importe quel type de gestionnaire de ressource via un type commun, avant je la déclarais dans la classe application, ainsi, je n'avais plus besoin de faire de singleton, mais ensuite je suis vite repasser par un singleton car je trouvais pas cela pas très pratique, de plus, le cache n'est pas la seule classe ayant besoin d'avoir accès aux ressources, mais tout les autres pointeurs intelligent ont aussi besoin d'y accéder donc, il fallait que je fasse une variable globale pour chaque type de gestionnaire de ressources, pas le choix, car le type du paramètre template de la ressource n'est défini dans la classe application, c'est au développeur lui même à le définir lors de la création des caches qui se partagent toutes les ressources de l'application. (C'est à dire tout les pointeurs)
    Rien que cela me fait craindre un très gros problème de conception...

    Je ne connais pas ton projet (et je ne m'attend pas à ce que tu nous l'expose complet), mais, de manière générale, je suis persuadé que les principes SOLID sont mis à mal dans une grande partie de ton code

    De manière générale, tu as tes données business (métier) qui ne doivent avoir de dépendances que vis à vis de types primitifs et / ou fournis par la bibliothèque standard de ton langage de programmation.

    Autour de tes données métier, tu dois avoir d'autres éléments (les vues) qui, à la limite, pourraient très bien ignorer purement et simplement les données business pour ne connaitre que des types simples, afin de donner la représentation des données business et, entre les deux, tu as juste une série de "controleurs" qui s'assurent que les ordres données par les vues sont cohérentes et valides et qui feront le lien entre la (les) vue(s) et les données business.

    Ajoute à cela que, en théorie, si tu as plusieurs trheads, un seul devrait prendre en charge la modification de tes données, les autres ne pouvant au mieu que les interroger sur leur état et le fait que certains thread (son, par exemple) n'ont besoin que de signaux comme "sort158 lancé" pour savoir comment il doivent réagir, et tu remarqueras que tout devient finalement beaucoup plus simple
    Pour les id (et non des pointeurs sur les id) se serait lourd car il faudrait remettre à jour les ids manuellement dans tout les caches à chaque fois que j'erase une ressource dans le vecteur du gestionnaire de ressources. Je ne crois pas que ça serait très performant de devoir remettre à jour tout cela manuellement hors que avec les pointeurs tu changes la valeur à un endroit et elle est automatiquement changée à tout les autres endroits du programme.
    C'est typiquement le symptome que tu n'as pas correctement centralisé la création et la libération de tes ressources.

    Typiquement, c'est un seul thread qui s'occupe de cela, dans une partie clairement déterminée de ton code... Du coup, tu transmets un tableau d'id (sans doute par référence pour qu'il puisse rester "à jour" par rapport à la réalité des faits et par référence constante pour t'assurer qu'il ne sera pas modifié), et tu es tranquille
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  16. #36
    Invité
    Invité(e)
    Par défaut
    Oui mais, ne serait ce pas trop lourd de devoir à chaque fois instancier un nouvel objet, pour les objets qui l'utilisent, ne vaut t'il pas mieux faire une variable globale qui, renvoie une référence sur l'objet dans le cas ou ce n'est pas nécessaire de créer cet objet plusieurs fois ?

    Pour les pointeurs sur les ids ce n'est pas un tableau d'ids, mais, un seul id que j'utilise.

    Et je ne fais aucune allocation ni désallocation, je passe juste l'id par référence constante, bref, si tu veux un brouillon j'en ai un ici :

    http://lolilolightdevblog.wordpress....core-partie-3/

    Bref j'essaye d'éviter au max les instanciations, et, de regrouper toutes les ressources d'un même type dans un seul contenaire que je partage entre plusieurs types de pointeur intelligent plutôt que de recréer un contenaire à chaque fois.
    Donc je ne fais que les allocations que pour la ressource en elle même, et je renvoie un pointeur sur sa position dans le std::vector, mais je n'alloue pas l'id dynamiquement.
    Et ensuite je n'ai qu'à renvoyé un pointeur sur la ressource en envoyant sa position dans le contenaire avec le pointeur intelligent, j'ai aussi ajouté la possibilité d'associer à chaque id, un ou plusieurs noms.
    Dernière modification par Invité ; 08/10/2014 à 14h44.

  17. #37
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Lolilolight Voir le message
    Oui mais, ne serait ce pas trop lourd de devoir à chaque fois instancier un nouvel objet, pour les objets qui l'utilisent, ne vaut t'il pas mieux faire une variable globale qui, renvoie une référence sur l'objet dans le cas ou ce n'est pas nécessaire de créer cet objet plusieurs fois ?
    Il ne s'agit pas d'instancier un nouvel objet à chaque fois qu'un autre élément veut l'utiliser, il s'agit d'instancier tes objet au moment le plus opportun puis de veiller à le transmettre à ceux qui en ont vraiment besoin
    Pour les pointeurs sur les ids ce n'est pas un tableau d'ids, mais, un seul id que j'utilise.
    A la base, chaque objet créé dont le type a sémantique d'entité devrait avoir une id qui lui est spécifique et qu'il garde "jusqu'à ce qu'il soit détruit". D'ailleurs, les ids ne devraient pas être réutilisées pour de nouveaux objets lorsque leur "propriétaire" d'origine a déjà été détruit.

    Quoi qu'il en soit,la très grosse majorité de tes threads ne devrait jamais avoir à modifier les objets qu'ils manipulent, quelle que soit la manière utilisée pour y accéder, et, encore moins modifier leur id qui correspond peu ou prou à la notion de clé primaire dans une base de données : si tu veux garantir ce qui se rapproche de l'unicité référentielle dans une base de données, la suppression d'un élément rend simplement une clé primaire invalide qui n'existera plus jamais. Tu dois partir d'un principe similaire pour tes différentes ids
    Bref j'essaye d'éviter au max les instanciations, et, de regrouper toutes les ressources d'un même type dans un seul contenaire que je partage entre plusieurs types de pointeur intelligent plutôt que de recréer un contenaire à chaque fois.
    ET ce faisant, tu jettes le SRP aux orties et te prépare des m...de monstres pour les évolutions futures!

    Il est cohérent de vouloir regrouper les ressources identiques (ex : les véhicules avec les véhicules, les végétaux avec les végétaux, les sons avec les sons, les textures avec les textures et ainsi de suite). Mais si tu dois maintenir d'une manière ou d'une autre des véhicules, des végétaux, des sons et des textures, il faut que tu respecte le SRP en créant une classe qui s'occupe de maintenir (et de te donner acces en cas de besoin) aux véhicules, une autre qui fasse pareil pour les végétaux, une troisième qui fera pareil pour les textures et une dernière qui agira de la sorte pour les sons.

    Et, bien sur, si certaines de ces ressources présentent des comportements polymorphes, il faudra également prévoir une fabrique afin de séparer la responsabilité de la création des objets de celle de leur maintien.

    Après, s'il apparait que les différents types de ressources doivent être accessibles au départ d'un même contexte (ce sera sans doute le cas pour les végétaux et les véhicules), tu fais en sorte d'instancier la classe qui s'occupe de leur maintien en mémoire dans la classe qui correspond au contexte dans lequel tu es sensé avoir besoin de ces ressources.

    Ainsi, tu instancieras sans doute la classe qui s'occupe du maintien et de l'acces aux texture dans la classe qui correspond au contexte de l'affichage, celles qui s'occupe du maintien des son dans la classe qui correspond au contexte du rendu sonore et les deux classes qui s'occupent du maintien des véhicules et des végétaux dans la classe qui correspond à un contexte tout à fait général (la classe Application )

    Donc je ne fais que les allocations que pour la ressource en elle même, et je renvoie un pointeur sur sa position dans le std::vector, mais je n'alloue pas l'id dynamiquement.
    Encore heureux!!!

    Mais la question qui tue est alors : pourquoi un pointeur et non une référence, de préférence constante, ou tout simplement la valeur, vu que l'id est spécifique à chaque objet et que tu ne semble vouloir utiliser qu'un objet à la fois
    Et ensuite je n'ai qu'à renvoyé un pointeur sur la ressource en envoyant sa position dans le contenaire avec le pointeur intelligent
    Et la question est alors : pourquoi un pointeur intelligent, vu que par définition, les pointeurs intelligents sont des enveloppes RAIIsantes te permettant de déterminer le meilleur moment pour libérer de la mémoire allouée de manière dynamique (et éviter les fuites mémoires et tentative d'accès à des adresses invalidées)
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  18. #38
    Invité
    Invité(e)
    Par défaut
    Il ne s'agit pas d'instancier un nouvel objet à chaque fois qu'un autre élément veut l'utiliser, il s'agit d'instancier tes objet au moment le plus opportun puis de veiller à le transmettre à ceux qui en ont vraiment besoin.
    A la base, chaque objet créé dont le type a sémantique d'entité devrait avoir une id qui lui est spécifique et qu'il garde "jusqu'à ce qu'il soit détruit". D'ailleurs, les ids ne devraient pas être réutilisées pour de nouveaux objets lorsque leur "propriétaire" d'origine a déjà été détruit.

    Quoi qu'il en soit,la très grosse majorité de tes threads ne devrait jamais avoir à modifier les objets qu'ils manipulent, quelle que soit la manière utilisée pour y accéder, et, encore moins modifier leur id qui correspond peu ou prou à la notion de clé primaire dans une base de données : si tu veux garantir ce qui se rapproche de l'unicité référentielle dans une base de données, la suppression d'un élément rend simplement une clé primaire invalide qui n'existera plus jamais. Tu dois partir d'un principe similaire pour tes différentes ids
    Ha ok donc selon doit je devrais instancier les classes qui maintienne une ressource dans le contexte qui la gère.

    Il est vrai que on n'a à l'instancier que lorsque l'on a besoin oui, et que peu de threads devraient modifier les ressources, mais bon, je pars du principe qu'il ne faille pas faire confiance à l'utilisateur et qu'il peut faire n'importe quoi avec la lib.

    Au niveau base de donnée du ne dois pas connaître tout les systèmes de gestion de bases de données car il y en a qui réutilise les ids (Sybase) et d'autres qui les rendent invalide. (MySQL)
    J'ai voulu partir sur un système comme SyBase.

    ET ce faisant, tu jettes le SRP aux orties et te prépare des m...de monstres pour les évolutions futures!

    Il est cohérent de vouloir regrouper les ressources identiques (ex : les véhicules avec les véhicules, les végétaux avec les végétaux, les sons avec les sons, les textures avec les textures et ainsi de suite). Mais si tu dois maintenir d'une manière ou d'une autre des véhicules, des végétaux, des sons et des textures, il faut que tu respecte le SRP en créant une classe qui s'occupe de maintenir (et de te donner acces en cas de besoin) aux véhicules, une autre qui fasse pareil pour les végétaux, une troisième qui fera pareil pour les textures et une dernière qui agira de la sorte pour les sons.

    Et, bien sur, si certaines de ces ressources présentent des comportements polymorphes, il faudra également prévoir une fabrique afin de séparer la responsabilité de la création des objets de celle de leur maintien.

    Après, s'il apparait que les différents types de ressources doivent être accessibles au départ d'un même contexte (ce sera sans doute le cas pour les végétaux et les véhicules), tu fais en sorte d'instancier la classe qui s'occupe de leur maintien en mémoire dans la classe qui correspond au contexte dans lequel tu es sensé avoir besoin de ces ressources.

    Ainsi, tu instancieras sans doute la classe qui s'occupe du maintien et de l'acces aux texture dans la classe qui correspond au contexte de l'affichage, celles qui s'occupe du maintien des son dans la classe qui correspond au contexte du rendu sonore et les deux classes qui s'occupent du maintien des véhicules et des végétaux dans la classe qui correspond à un contexte tout à fait général (la classe Application )
    Oui ça s'est ce que je fais pour tout les autres types d'objets c'est à dire, ceux utilisant les ressources.

    Encore heureux!!!

    Mais la question qui tue est alors : pourquoi un pointeur et non une référence, de préférence constante, ou tout simplement la valeur, vu que l'id est spécifique à chaque objet et que tu ne semble vouloir utiliser qu'un objet à la fois
    Non j'en utilise plusieurs à la fois, qui partagent le même gestionnaire de ressource, et, lorsque j'affecte deux objets qui, ne pointent pas vers la même ressource, alors là je dois modifier le pointeur, je ne peux donc pas faire de référence ou bien de pointeurs constant.

    Et la question est alors : pourquoi un pointeur intelligent, vu que par définition, les pointeurs intelligents sont des enveloppes RAIIsantes te permettant de déterminer le meilleur moment pour libérer de la mémoire allouée de manière dynamique (et éviter les fuites mémoires et tentative d'accès à des adresses invalidées)
    Car ce sont les pointeurs intelligent qui accèdent aux ressources et les libères quand cela est nécessaire pour éviter les fuites de mémoire et l'accès à des pointeurs invalides.

  19. #39
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Lolilolight Voir le message
    Ha ok donc selon doit je devrais instancier les classes qui maintienne une ressource dans le contexte qui la gère.
    Bien sur!!! Il y a quantité de ressources qui ne sont réellement nécessaires que dans un contexte donné!!! il n'y a aucune raison à se trimbaler ces ressources au niveau de l'application

    Prenons un exemple simple : un jeu d'échec affiché grace à OpenGL pour lequel on utiliserait des fichiers obj pour décrire la manière dont les pièces sont affichées.

    Au niveau du business, tu aurais une classe de base ChessPiece qui pourrait ressembler à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    enum Color{
        colorNone,
        colorWhite,
        colorBlack,
        COLORMAX
    };
    class PieceVisitor;
    class ChessPiece/* : private NonCopyable*/{
        public:
            ChessPiece(Color color):color_(color){ assert(color_>colorNone && color_<COLORMAX);}
            virtual ~ChessPiece();
            Color color() const{return color_;}
            virtual bool accept(PieceVisitor const & ) const = 0;
            /* ... */
        private:
            Color color_;
    };
    A partir de là, on crée une classe pour chaque type de pièce qui hérite de ChessPiece sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Pawn : public ChessPiece{
        public:
            Pawn(Color color):ChessPiece(color){}
            bool acept(PieceVisitor const & v) const override{retrun v.visit(*this);}
    };
    /* idem pour Tower, Knight, Bischop, Queen et King */
    Ca, ce sont des classes qui font partie de ton business, elles seront instanciée dans une classe Game en même temps que ta classe ChessBoard, parce que c'est ta classe Game qui s'occupera de la partie

    Dans cette classe Game, tu retrouve également une instance de GlContext dont le but est de créer et de maintenir le contexte OpenGL. D'une manière ou d'une autre, cette classe délègue à d'autres le soin de charger les fichiers obj, de créer les textures et de maintenir les vertexes utilisés par OpenGL pour tout ce petit monde.

    Vu de l'extérieur, tout ce que le contexte OpenGL doit exposer à la partie, c'est un accesseur sur un objet de type Drawer qui hérite de PieceVisitor et qui, pourrait prendre une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    Drawer :public PieceVisitor{
        public:
            Drawer(GlContext /* const */ & context):context_(context){}
            void drawBoard(ChessBoard const & board) const{
                for(auto const & c : board){
                    /*afficher la case sur laquelle on passe */
                    if(c.pieceColor() != colorNone){
                        coord_=c.coordinate();
                        c.piece().accept(*this);
                    }
               }
            }
            bool accept(Pawn const & p) const override{
                context_.draw(pion, p.color(), coord_);
            }
            bool accept(tower const & t) const override{
                context_.draw(tower, t.color(), coord_);
            }
            /* idem pour les autres */
        private:
            GlContext context_;
            Coordinate coord_;
    };
    Peu importe à quoi correspondent la donnée tower, pawn et les autres transmises au contexte (est-ce des ids? des std::string?), ce qui importe c'est que c'est une donnée qui permet au contexte OpenGL de sélectionner l'objet qui sera affiché ;-)

    Il est vrai que on n'a à l'instancier que lorsque l'on a besoin oui, et que peu de threads devraient modifier les ressources, mais bon, je pars du principe qu'il ne faille pas faire confiance à l'utilisateur et qu'il peut faire n'importe quoi avec la lib.
    Tu es malheureusement obligé de lui faire un minimum confiance... Et pour que cette relation de confiance puisse exister, un maitre mot : programmation par contrat

    Définis clairement les préconditions, les postconditions et les invariants. Vérifies les précondition et les invariants si possible à la compilation (static_assert) et au plus tard à l'exécution (assert), lances une exception si les posts conditions ne sont pas remplies et... Roulez jeunesse

    Au niveau base de donnée du ne dois pas connaître tout les systèmes de gestion de bases de données car il y en a qui réutilise les ids (Sybase) et d'autres qui les rendent invalide. (MySQL)
    J'ai voulu partir sur un système comme SyBase.
    Avec le risque qu'un utilisateur dispose d'une version obsolète des ids

    Je te l'ai dit : je ne connais pas le but ultime de ton projet... Des ids réutilisables sont envisageables. Le problème est peut être d'arriver à garder la cohérences référentielle (que tout ce qui dépend d'une id donnée est supprimé lorsque l'id est supprimée) avec effacements en cascades... C'est pas forcément ce qu'il y a de plus facile
    Oui ça s'est ce que je fais pour tout les autres types d'objets c'est à dire, ceux utilisant les ressources.
    Ben, fais le aussi pour tes objets qui ont pour responsabilité de maintenir tes ressources... Tu ne t'en sentira que mieux
    Non j'en utilise plusieurs à la fois, qui partagent le même gestionnaire de ressource, et, lorsque j'affecte deux objets qui, ne pointent pas vers la même ressource, alors là je dois modifier le pointeur, je ne peux donc pas faire de référence ou bien de pointeurs constant.
    C'est bien pour cela que tu dois veiller à centraliser l'endroit où la ressource (les objets) sont affectés, et faire en sorte qu'il n'y ait qu'un thread (celui qui manipule l'instance de la classe qui a pour but de maintenir tes objets / ressources en vie) qui puisse créer / affecter des objets, je ne vois pas pourquoi tu te complique la vie
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  20. #40
    Invité
    Invité(e)
    Par défaut
    Ok, bah dans ce cas je n'ai qu'à, changer le singleton, et déclarer le gestionnaire de ressources en l'utilisant seulement là ou j'en ai besoin, et rendre les ids invalide plutôt que de les supprimer.

    Bref si j'ai fais ça c'est parce que je ne voulais pas faire de trous en laissant des ids invalide alors j'ai fais en sorte qu'on ne puisse créer qu'une seule instance d'un même type du gestionnaire de ressource, de ce fait, je n'avais pas à remettre à jour les ids pour toutes les instances d'un même type de gestionnaire de ressources utilisée à chaque fois que je supprimais une ressource.

    Car comme tu le dis ça aurait été plus compliqué.j'aurai du faire des effacements en cascade..., donc, j'ai préféré faire un singleton (malgré tout le mal que l'on dit à son sujet) plutôt que de faire un système qui efface les ids en cascade et qui me semble, fort compliqué à faire.

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 3 PremièrePremière 123 DernièreDernière

Discussions similaires

  1. Réponses: 8
    Dernier message: 29/07/2013, 17h30
  2. Réponses: 1
    Dernier message: 26/02/2008, 12h54
  3. Réponses: 15
    Dernier message: 25/06/2007, 09h35
  4. Réponses: 2
    Dernier message: 06/05/2007, 12h52
  5. Réponses: 2
    Dernier message: 26/09/2006, 08h56

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo