Welcome to mirror list, hosted at ThFree Co, Russian Federation.

BLI_resource_scope.hh « blenlib « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: edffb148477fd0d8a485bb4f7766a4be7bc59bff (plain)
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
/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#pragma once

/** \file
 * \ingroup bli
 *
 * A `ResourceScope` takes ownership of arbitrary data/resources. Those resources will be
 * destructed and/or freed when the `ResourceScope` is destructed. Destruction happens in reverse
 * order. That allows resources do depend on other resources that have been added before.
 *
 * A `ResourceScope` can also be thought of as a dynamic/runtime version of normal scopes in C++
 * that are surrounded by braces.
 *
 * The main purpose of a `ResourceScope` is to allow functions to inject data into the scope of the
 * caller. Traditionally, that can only be done by returning a value that owns everything it needs.
 * This is fine until one has to deal with optional ownership. There are many ways to have a type
 * optionally own something else, all of which are fairly annoying. A `ResourceScope` can be used
 * to avoid having to deal with optional ownership. If some value would be owned, it can just be
 * added to the resource scope, otherwise not.
 *
 * When a function takes a `ResourceScope` as parameter, it usually means that its return value
 * will live at least as long as the passed in resources scope. However, it might also live longer.
 * That can happen when the function returns a reference to statically allocated data or
 * dynamically allocated data depending on some condition.
 */

#include "BLI_linear_allocator.hh"
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"

namespace blender {

class ResourceScope : NonCopyable, NonMovable {
 private:
  struct ResourceData {
    void *data;
    void (*free)(void *data);
  };

  LinearAllocator<> allocator_;
  Vector<ResourceData> resources_;

 public:
  ResourceScope() = default;

  ~ResourceScope()
  {
    /* Free in reversed order. */
    for (int64_t i = resources_.size(); i--;) {
      ResourceData &data = resources_[i];
      data.free(data.data);
    }
  }

  /**
   * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when
   * the collector is destructed.
   */
  template<typename T> T *add(std::unique_ptr<T> resource)
  {
    BLI_assert(resource.get() != nullptr);
    T *ptr = resource.release();
    if (ptr == nullptr) {
      return nullptr;
    }
    this->add(ptr, [](void *data) {
      T *typed_data = reinterpret_cast<T *>(data);
      delete typed_data;
    });
    return ptr;
  }

  /**
   * Pass ownership of the resource to the ResourceScope. It will be destructed when the
   * collector is destructed.
   */
  template<typename T> T *add(destruct_ptr<T> resource)
  {
    T *ptr = resource.release();
    if (ptr == nullptr) {
      return nullptr;
    }
    /* There is no need to keep track of such types. */
    if (std::is_trivially_destructible_v<T>) {
      return ptr;
    }

    this->add(ptr, [](void *data) {
      T *typed_data = reinterpret_cast<T *>(data);
      typed_data->~T();
    });
    return ptr;
  }

  /**
   * Pass ownership of some resource to the ResourceScope. The given free function will be
   * called when the collector is destructed.
   */
  void add(void *userdata, void (*free)(void *))
  {
    ResourceData data;
    data.data = userdata;
    data.free = free;
    resources_.append(data);
  }

  /**
   * Construct an object with the same value in the ResourceScope and return a reference to the
   * new value.
   */
  template<typename T> T &add_value(T &&value)
  {
    return this->construct<T>(std::forward<T>(value));
  }

  /**
   * The passed in function will be called when the scope is destructed.
   */
  template<typename Func> void add_destruct_call(Func func)
  {
    void *buffer = allocator_.allocate(sizeof(Func), alignof(Func));
    new (buffer) Func(std::move(func));
    this->add(buffer, [](void *data) { (*(Func *)data)(); });
  }

  /**
   * Returns a reference to a linear allocator that is owned by the ResourcesCollector. Memory
   * allocated through this allocator will be freed when the collector is destructed.
   */
  LinearAllocator<> &linear_allocator()
  {
    return allocator_;
  }

  /**
   * Utility method to construct an instance of type T that will be owned by the ResourceScope.
   */
  template<typename T, typename... Args> T &construct(Args &&...args)
  {
    destruct_ptr<T> value_ptr = allocator_.construct<T>(std::forward<Args>(args)...);
    T &value_ref = *value_ptr;
    this->add(std::move(value_ptr));
    return value_ref;
  }
};

}  // namespace blender