1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2016 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17/** \file
18 * This file is internal to the inner workings of
19 * Phosphor and is not intended for public consumption.
20 */
21
22#pragma once
23
24#include <sstream>
25#include <stdexcept>
26#include <string>
27#include <type_traits>
28
29#include "inline_zstring.h"
30#include "platform/core.h"
31
32namespace phosphor {
33
34    /**
35     * Special empty tag type used to signify that no argument has been given
36     */
37    struct NoneType {
38    };
39
40    /**
41     * A union which represents a single trace argument.
42     *
43     * Has various methods / constructors for tidily creating
44     * and printing the TraceArgument.
45     */
46    union TraceArgument {
47        /**
48         * Enumeration of the possible types of a TraceArgument
49         */
50        enum class Type : char {
51            is_bool,
52            is_uint,
53            is_int,
54            is_double,
55            is_pointer,
56            is_string,
57            is_istring,
58            is_none
59        };
60
61        bool as_bool;
62        unsigned long long as_uint;
63        long long as_int;
64        double as_double;
65        const char* as_string;
66        const void* as_pointer;
67        inline_zstring<8> as_istring;
68        NoneType as_none;
69
70        /**
71         * Default constructor
72         */
73        TraceArgument() = default;
74
75        /**
76         * Templated conversion constructor
77         *
78         * @param src Value to be converted to a TraceArgument
79         *
80         * Usage:
81         *
82         *     TraceArgument trace_arg = TraceArgument(arg);
83         *
84         * Where arg is one of many primitive types, e.g. int.
85         *
86         */
87        template <class T>
88        inline constexpr TraceArgument(T src);
89
90        /**
91         * Converts the TraceArgument to string
92         *
93         * @param type The enum which describes the type of the TraceArgument
94         * @return String form of the TraceArgument as the given type
95         */
96        inline std::string to_string(TraceArgument::Type type) const;
97
98    private:
99        template <TraceArgument::Type T>
100        inline std::string internal_to_string();
101    };
102
103    /**
104     * Utility class for holding functions useful for
105     * templated argument conversions.
106     */
107    template <typename T>
108    class TraceArgumentConversion {
109    public:
110        /**
111         * @return The enum-form type of the argument
112         */
113        inline static constexpr TraceArgument::Type getType();
114
115        /**
116         * @param T The argument to be converted to a TraceArgument
117         * @return The argument wrapped as a TraceArgument
118         */
119        inline static constexpr TraceArgument asArgument(T arg);
120    };
121
122    static_assert(sizeof(TraceArgument) <= 8,
123                  "TraceArgument must be 8 or less bytes");
124
125/**
126 * Used for defining the constructor and type-to-enum
127 * constexpr for a given type.
128 *
129 * @param src The origin type of the argument
130 * @param dst The destination 'type' (aka the appropriate is_/as_
131 *            suffix) of the argument.
132 */
133#define ARGUMENT_CONVERSION(src, dst) \
134    template <> \
135    inline constexpr TraceArgument::TraceArgument(src arg) : as_##dst(arg) {} \
136    template<> \
137    class TraceArgumentConversion<src> { \
138        public:\
139        inline static constexpr TraceArgument::Type getType() { \
140            return TraceArgument::Type::is_##dst; \
141        } \
142        \
143        inline static constexpr TraceArgument asArgument(src arg) { \
144            return TraceArgument(arg); \
145        } \
146    };
147
148    ARGUMENT_CONVERSION(bool, bool)
149
150    ARGUMENT_CONVERSION(char, int)
151
152    ARGUMENT_CONVERSION(short, int)
153
154    ARGUMENT_CONVERSION(int, int)
155
156    ARGUMENT_CONVERSION(long, int)
157
158    ARGUMENT_CONVERSION(long long, int)
159
160    ARGUMENT_CONVERSION(unsigned char, uint)
161
162    ARGUMENT_CONVERSION(unsigned short, uint)
163
164    ARGUMENT_CONVERSION(unsigned int, uint)
165
166    ARGUMENT_CONVERSION(unsigned long, uint)
167
168    ARGUMENT_CONVERSION(unsigned long long, uint)
169
170    ARGUMENT_CONVERSION(float, double)
171
172    ARGUMENT_CONVERSION(double, double)
173
174    ARGUMENT_CONVERSION(const void*, pointer)
175
176    ARGUMENT_CONVERSION(const char*, string)
177
178    ARGUMENT_CONVERSION(inline_zstring<8>, istring)
179
180    ARGUMENT_CONVERSION(NoneType, none)
181
182#undef ARGUMENT_CONVERSION
183
184    /**
185     * Partial specialization argument conversion for any pointer type
186     */
187    template<typename T>
188    class TraceArgumentConversion<T*> {
189        public:
190
191        inline static constexpr TraceArgument::Type getType() {
192            return TraceArgument::Type::is_pointer;
193        }
194
195        inline static constexpr TraceArgument asArgument(T* arg) {
196
197            // This relies on the 'const void*' -> pointer
198            // conversion defined above.
199            return TraceArgument(static_cast<const void*>(arg));
200        }
201    };
202
203    inline std::string TraceArgument::to_string(
204        TraceArgument::Type type) const {
205        std::stringstream ss;
206        switch (type) {
207        case Type::is_bool:
208            return as_bool ? "true" : "false";
209        case Type::is_int:
210            return std::to_string(as_int);
211        case Type::is_uint:
212            return std::to_string(as_uint);
213        case Type::is_double:
214            return std::to_string(as_double);
215        case Type::is_pointer:
216            ss << as_pointer;
217            return "\"" + ss.str() + "\"";
218        case Type::is_string:
219            return "\"" + std::string(as_string) + "\"";
220        case Type::is_istring:
221            return "\"" + std::string(as_istring) + "\"";
222        case Type::is_none:
223            return std::string("\"Type::is_none\"");
224        }
225        throw std::invalid_argument("Invalid TraceArgument type");
226    }
227}  // namespace phosphor
228