diff --git a/.gitignore b/.gitignore index 60996a5b..3aac9ad8 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,9 @@ matlab_runners/Feature Point Experiments/yt_features/ matlab_runners/Feature Point Experiments/yt_features_clm/ matlab_runners/Gaze Experiments/mpii_out/ build/ +Debug/ +Release/AU_predictors/ +Release/ +ipch/ +matlab_runners/Demos/output_features_seq/ +matlab_runners/Demos/output_features_vid/ diff --git a/OpenFace.sln b/OpenFace.sln index 84732ad4..73c1f0b0 100644 --- a/OpenFace.sln +++ b/OpenFace.sln @@ -13,7 +13,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FeatureExtraction", "exe\Fe EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{99FEBA13-BDDF-4076-B57E-D8EF4076E20D}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Executables", "Executables", "{9961DDAC-BE6E-4A6E-8EEF-FFC7D67BD631}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Command line Executables", "Command line Executables", "{9961DDAC-BE6E-4A6E-8EEF-FFC7D67BD631}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FaceLandmarkVidMulti", "exe\FaceLandmarkVidMulti\FaceLandmarkVidMulti.vcxproj", "{C3FAF36F-44BC-4454-87C2-C5106575FE50}" EndProject @@ -23,6 +23,16 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FaceLandmarkVid", "exe\Face EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FaceLandmarkImg", "exe\FaceLandmarkImg\FaceLandmarkImg.vcxproj", "{DDC3535E-526C-44EC-9DF4-739E2D3A323B}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GUI", "GUI", "{C9D8D0B0-11EC-41CB-8524-2DDB5BE94297}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppInerop", "lib\local\CppInerop\CppInerop.vcxproj", "{78196985-EE54-411F-822B-5A23EDF80642}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UtilLibs", "UtilLibs", "{D5F7B3E2-01FE-4F4A-8CE1-274D74D395D4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CamCom", "lib\local\CamCom\CamCom.vcxproj", "{0CEC6A75-17BA-4DC5-9405-C74154921E60}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenFaceOffline", "gui\OpenFaceOffline\OpenFaceOffline.csproj", "{A4760F41-2B1F-4144-B7B2-62785AFFE79B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -95,12 +105,36 @@ Global {DDC3535E-526C-44EC-9DF4-739E2D3A323B}.Release|Win32.Build.0 = Release|Win32 {DDC3535E-526C-44EC-9DF4-739E2D3A323B}.Release|x64.ActiveCfg = Release|x64 {DDC3535E-526C-44EC-9DF4-739E2D3A323B}.Release|x64.Build.0 = Release|x64 + {78196985-EE54-411F-822B-5A23EDF80642}.Debug|Win32.ActiveCfg = Debug|Win32 + {78196985-EE54-411F-822B-5A23EDF80642}.Debug|Win32.Build.0 = Debug|Win32 + {78196985-EE54-411F-822B-5A23EDF80642}.Debug|x64.ActiveCfg = Debug|x64 + {78196985-EE54-411F-822B-5A23EDF80642}.Debug|x64.Build.0 = Debug|x64 + {78196985-EE54-411F-822B-5A23EDF80642}.Release|Win32.ActiveCfg = Release|Win32 + {78196985-EE54-411F-822B-5A23EDF80642}.Release|Win32.Build.0 = Release|Win32 + {78196985-EE54-411F-822B-5A23EDF80642}.Release|x64.ActiveCfg = Release|x64 + {78196985-EE54-411F-822B-5A23EDF80642}.Release|x64.Build.0 = Release|x64 + {0CEC6A75-17BA-4DC5-9405-C74154921E60}.Debug|Win32.ActiveCfg = Debug|Win32 + {0CEC6A75-17BA-4DC5-9405-C74154921E60}.Debug|Win32.Build.0 = Debug|Win32 + {0CEC6A75-17BA-4DC5-9405-C74154921E60}.Debug|x64.ActiveCfg = Debug|x64 + {0CEC6A75-17BA-4DC5-9405-C74154921E60}.Debug|x64.Build.0 = Debug|x64 + {0CEC6A75-17BA-4DC5-9405-C74154921E60}.Release|Win32.ActiveCfg = Release|Win32 + {0CEC6A75-17BA-4DC5-9405-C74154921E60}.Release|Win32.Build.0 = Release|Win32 + {0CEC6A75-17BA-4DC5-9405-C74154921E60}.Release|x64.ActiveCfg = Release|x64 + {0CEC6A75-17BA-4DC5-9405-C74154921E60}.Release|x64.Build.0 = Release|x64 + {A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Debug|Win32.ActiveCfg = Debug|x86 + {A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Debug|Win32.Build.0 = Debug|x86 + {A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Debug|x64.ActiveCfg = Debug|Any CPU + {A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Debug|x64.Build.0 = Debug|Any CPU + {A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Release|Win32.ActiveCfg = Release|Any CPU + {A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Release|Win32.Build.0 = Release|Any CPU + {A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Release|x64.ActiveCfg = Release|Any CPU + {A4760F41-2B1F-4144-B7B2-62785AFFE79B}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {B47A5F12-2567-44E9-AE49-35763EC82149} = {99FEBA13-BDDF-4076-B57E-D8EF4076E20D} + {B47A5F12-2567-44E9-AE49-35763EC82149} = {D5F7B3E2-01FE-4F4A-8CE1-274D74D395D4} {BDC1D107-DE17-4705-8E7B-CDDE8BFB2BF8} = {99FEBA13-BDDF-4076-B57E-D8EF4076E20D} {0E7FC556-0E80-45EA-A876-DDE4C2FEDCD7} = {99FEBA13-BDDF-4076-B57E-D8EF4076E20D} {8A23C00D-767D-422D-89A3-CF225E3DAB4B} = {9961DDAC-BE6E-4A6E-8EEF-FFC7D67BD631} @@ -108,5 +142,8 @@ Global {2D80FA0B-2DE8-4475-BA5A-C08A9E1EDAAC} = {9961DDAC-BE6E-4A6E-8EEF-FFC7D67BD631} {34032CF2-1B99-4A25-9050-E9C13DD4CD0A} = {9961DDAC-BE6E-4A6E-8EEF-FFC7D67BD631} {DDC3535E-526C-44EC-9DF4-739E2D3A323B} = {9961DDAC-BE6E-4A6E-8EEF-FFC7D67BD631} + {78196985-EE54-411F-822B-5A23EDF80642} = {D5F7B3E2-01FE-4F4A-8CE1-274D74D395D4} + {0CEC6A75-17BA-4DC5-9405-C74154921E60} = {D5F7B3E2-01FE-4F4A-8CE1-274D74D395D4} + {A4760F41-2B1F-4144-B7B2-62785AFFE79B} = {C9D8D0B0-11EC-41CB-8524-2DDB5BE94297} EndGlobalSection EndGlobal diff --git a/gui/OpenFace-offline/App.config b/gui/OpenFace-offline/App.config new file mode 100644 index 00000000..88fa4027 --- /dev/null +++ b/gui/OpenFace-offline/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/gui/OpenFace-offline/App.xaml b/gui/OpenFace-offline/App.xaml new file mode 100644 index 00000000..a7c9cabe --- /dev/null +++ b/gui/OpenFace-offline/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/gui/OpenFace-offline/App.xaml.cs b/gui/OpenFace-offline/App.xaml.cs new file mode 100644 index 00000000..21fd7428 --- /dev/null +++ b/gui/OpenFace-offline/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/gui/OpenFace-offline/FpsTracker.cs b/gui/OpenFace-offline/FpsTracker.cs new file mode 100644 index 00000000..a7f2ee7b --- /dev/null +++ b/gui/OpenFace-offline/FpsTracker.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; + +namespace OpenFaceOffline +{ + public class FpsTracker + { + public TimeSpan HistoryLength { get; set; } + public FpsTracker() + { + HistoryLength = TimeSpan.FromSeconds(2); + } + + private Queue frameTimes = new Queue(); + + private void DiscardOldFrames() + { + while (frameTimes.Count > 0 && (MainWindow.CurrentTime - frameTimes.Peek()) > HistoryLength) + frameTimes.Dequeue(); + } + + public void AddFrame() + { + frameTimes.Enqueue(MainWindow.CurrentTime); + DiscardOldFrames(); + } + + public double GetFPS() + { + DiscardOldFrames(); + + if (frameTimes.Count == 0) + return 0; + + return frameTimes.Count / (MainWindow.CurrentTime - frameTimes.Peek()).TotalSeconds; + } + } +} diff --git a/gui/OpenFace-offline/MainWindow.xaml b/gui/OpenFace-offline/MainWindow.xaml new file mode 100644 index 00000000..ea38aadf --- /dev/null +++ b/gui/OpenFace-offline/MainWindow.xaml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appearance features + + + + + + + + + + + + + + + + + + + + + Geometry features + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Action Units + + + + + Classification + + + + + + + Regression + + + + + + + + + + + + + + + diff --git a/gui/OpenFace-offline/MainWindow.xaml.cs b/gui/OpenFace-offline/MainWindow.xaml.cs new file mode 100644 index 00000000..2417eb0a --- /dev/null +++ b/gui/OpenFace-offline/MainWindow.xaml.cs @@ -0,0 +1,1249 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Microsoft.Win32; + +using OpenCVWrappers; +using CppInterop; +using CppInterop.LandmarkDetector; +using CameraInterop; +using FaceAnalyser_Interop; +using System.Windows.Threading; +using System.IO; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + + // Timing for measuring FPS + #region High-Resolution Timing + static DateTime startTime; + static Stopwatch sw = new Stopwatch(); + + static MainWindow() + { + startTime = DateTime.Now; + sw.Start(); + } + + public static DateTime CurrentTime + { + get { return startTime + sw.Elapsed; } + } + #endregion + + Thread processing_thread; + + // Some members for displaying the results + private Capture capture; + private WriteableBitmap latest_img; + private WriteableBitmap latest_aligned_face; + private WriteableBitmap latest_HOG_descriptor; + + // Managing the running of the analysis system + private volatile bool thread_running; + private volatile bool thread_paused = false; + // Allows for going forward in time step by step + // Useful for visualising things + private volatile int skip_frames = 0; + + FpsTracker processing_fps = new FpsTracker(); + + volatile bool detectionSucceeding = false; + + volatile bool reset = false; + + // For tracking + FaceModelParameters clnf_params; + CLNF clnf_model; + FaceAnalyserManaged face_analyser; + + // Recording parameters (default values) + bool record_aus = false; // Recording Action Units + bool record_pose = false; // head location and orientation + bool record_params = false; // rigid and non-rigid shape parameters + bool record_2D_landmarks = false; // 2D landmark location + bool record_3D_landmarks = false; // 3D landmark locations in world coordinates + bool record_HOG = false; // HOG features extracted from face images + bool record_gaze = false; // Gaze recording + bool record_aligned = false; // aligned face images + bool record_tracked_vid = false; + + // Visualisation options + bool show_tracked_video = true; + bool show_appearance = true; + bool show_geometry = true; + bool show_aus = true; + + // TODO classifiers converted to regressors + + // TODO indication that track is done + + // The recording managers + StreamWriter output_head_pose_file; + StreamWriter output_clnf_params_file; + StreamWriter output_2D_landmarks_file; + StreamWriter output_3D_landmarks_file; + StreamWriter output_au_class; + StreamWriter output_au_reg; + StreamWriter output_gaze; + + // Where the recording is done (by default in a record directory, from where the application executed) + String record_root = "./record"; + + // For AU visualisation and output + List au_class_names; + List au_reg_names; + + // For AU prediction + bool dynamic_AU_shift = true; + bool dynamic_AU_scale = false; + bool use_dynamic_models = true; + + private volatile bool mirror_image = false; + + public MainWindow() + { + InitializeComponent(); + + // Set the icon + Uri iconUri = new Uri("logo1.ico", UriKind.RelativeOrAbsolute); + this.Icon = BitmapFrame.Create(iconUri); + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 2000), (Action)(() => + { + RecordAUCheckBox.IsChecked = record_aus; + RecordAlignedCheckBox.IsChecked = record_aligned; + RecordTrackedVidCheckBox.IsChecked = record_tracked_vid; + RecordHOGCheckBox.IsChecked = record_HOG; + RecordGazeCheckBox.IsChecked = record_gaze; + RecordLandmarks2DCheckBox.IsChecked = record_2D_landmarks; + RecordLandmarks3DCheckBox.IsChecked = record_3D_landmarks; + RecordParamsCheckBox.IsChecked = record_params; + RecordPoseCheckBox.IsChecked = record_pose; + + UseDynamicModelsCheckBox.IsChecked = use_dynamic_models; + UseDynamicScalingCheckBox.IsChecked = dynamic_AU_scale; + UseDynamicShiftingCheckBox.IsChecked = dynamic_AU_shift; + })); + + String root = AppDomain.CurrentDomain.BaseDirectory; + + clnf_params = new FaceModelParameters(root); + clnf_model = new CLNF(clnf_params); + face_analyser = new FaceAnalyserManaged(root, use_dynamic_models); + + } + + private bool ProcessFrame(CLNF clnf_model, FaceModelParameters clnf_params, RawImage frame, RawImage grayscale_frame, double fx, double fy, double cx, double cy) + { + detectionSucceeding = clnf_model.DetectLandmarksInVideo(grayscale_frame, clnf_params); + return detectionSucceeding; + + } + + private List>> ProcessImage(CLNF clnf_model, FaceModelParameters clnf_params, RawImage frame, RawImage grayscale_frame) + { + List>> landmark_detections = clnf_model.DetectMultiFaceLandmarksInImage(grayscale_frame, clnf_params); + return landmark_detections; + + } + + private void SetupRecording(String root, String filename, int width, int height) + { + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + RecordingMenu.IsEnabled = false; + UseDynamicModelsCheckBox.IsEnabled = false; + })); + + if (!System.IO.Directory.Exists(root)) + { + System.IO.Directory.CreateDirectory(root); + } + + if (record_pose) + { + String filename_poses = root + "/" + filename + ".pose"; + output_head_pose_file = new StreamWriter(filename_poses); + output_head_pose_file.WriteLine("frame,success,confidence,pose_X(mm),pose_Y(mm),pose_Z(mm),pitch(rad),yaw(rad),roll(rad)"); + } + + if (record_params) + { + String filename_params = root + "/" + filename + ".params"; + output_clnf_params_file = new StreamWriter(filename_params); + + output_clnf_params_file.Write("frame,success,confidence,scale,rot_x,rot_y,rot_z,tx,ty"); + for (int i = 0; i < clnf_model.GetNumModes(); ++i) + { + output_clnf_params_file.Write(",p" + i); + } + output_clnf_params_file.WriteLine(); + } + + if (record_2D_landmarks) + { + String filename_2d_landmarks = root + "/" + filename + ".landmarks_2d"; + output_2D_landmarks_file = new StreamWriter(filename_2d_landmarks); + + output_2D_landmarks_file.Write("frame,success,confidence"); + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_2D_landmarks_file.Write(",x" + i); + } + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_2D_landmarks_file.Write(",y" + i); + } + output_2D_landmarks_file.WriteLine(); + } + + if (record_aus) + { + String filename_au_class = root + "/" + filename + ".au_class"; + output_au_class = new StreamWriter(filename_au_class); + + output_au_class.Write("frame,success,confidence"); + au_class_names = face_analyser.GetClassActionUnitsNames(); + au_class_names.Sort(); + foreach (var name in au_class_names) + { + output_au_class.Write("," + name); + } + output_au_class.WriteLine(); + + String filename_au_reg = root + "/" + filename + ".au_reg"; + output_au_reg = new StreamWriter(filename_au_reg); + + output_au_reg.Write("frame,success,confidence"); + au_reg_names = face_analyser.GetRegActionUnitsNames(); + au_reg_names.Sort(); + foreach (var name in au_reg_names) + { + output_au_reg.Write("," + name); + } + output_au_reg.WriteLine(); + + } + + if (record_gaze) + { + String filename_gaze = root + "/" + filename + ".gaze"; + output_gaze = new StreamWriter(filename_gaze); + + output_gaze.Write("frame, success, confidence, x_0, y_0, z_0, x_1, y_1, z_1, x_h0, y_h0, z_h0, x_h1, y_h1, z_h1"); + } + + if (record_3D_landmarks) + { + String filename_3d_landmarks = root + "/" + filename + ".landmarks_3d"; + output_3D_landmarks_file = new StreamWriter(filename_3d_landmarks); + + output_3D_landmarks_file.Write("frame,success,confidence"); + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_3D_landmarks_file.Write(",X" + i); + } + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_3D_landmarks_file.Write(",Y" + i); + } + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_3D_landmarks_file.Write(",Z" + i); + } + output_3D_landmarks_file.WriteLine(); + } + + if (record_aligned) + { + String aligned_root = root + "/" + filename + "_aligned/"; + System.IO.Directory.CreateDirectory(aligned_root); + face_analyser.SetupAlignedImageRecording(aligned_root); + } + + if (record_tracked_vid) + { + String vid_loc = root + "/" + filename + ".avi"; + System.IO.Directory.CreateDirectory(root); + face_analyser.SetupTrackingRecording(vid_loc, width, height, 30); + } + + if (record_HOG) + { + String filename_HOG = root + "/" + filename + ".hog"; + face_analyser.SetupHOGRecording(filename_HOG); + } + + } + + private void StopRecording() + { + if (record_pose && output_head_pose_file != null) + output_head_pose_file.Close(); + + if (record_params && output_clnf_params_file != null) + output_clnf_params_file.Close(); + + if (record_2D_landmarks && output_2D_landmarks_file != null) + output_2D_landmarks_file.Close(); + + if (record_3D_landmarks && output_3D_landmarks_file != null) + output_3D_landmarks_file.Close(); + + if (record_gaze && output_gaze != null) + output_gaze.Close(); + + if (record_HOG) + face_analyser.StopHOGRecording(); + + if (record_tracked_vid) + face_analyser.StopTrackingRecording(); + + if (record_aus && output_au_class != null && output_au_reg != null) + { + output_au_class.Close(); + output_au_reg.Close(); + } + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + RecordingMenu.IsEnabled = true; + UseDynamicModelsCheckBox.IsEnabled = true; + + })); + + } + + // Recording the relevant objects + private void RecordFrame(CLNF clnf_model, bool success_b, int frame_ind, RawImage frame, RawImage grayscale_frame, double fx, double fy, double cx, double cy) + { + double confidence = (-clnf_model.GetConfidence()) / 2.0 + 0.5; + + List pose = new List(); + clnf_model.GetCorrectedPoseWorld(pose, fx, fy, cx, cy); + + int success = 0; + if (success_b) + success = 1; + + if (record_pose) + { + String pose_string = String.Format("{0},{1},{2:F3},{3:F3},{4:F3},{5:F3},{6:F3},{7:F3},{8:F3}", frame_ind, success, confidence, pose[0], pose[1], pose[2], pose[3], pose[4], pose[5]); + output_head_pose_file.WriteLine(pose_string); + } + + if (record_params) + { + output_clnf_params_file.Write(String.Format("{0},{1},{2,0:F3}", frame_ind, success, confidence)); + + List all_params = clnf_model.GetParams(); + + for (int i = 0; i < all_params.Count; ++i) + { + String param = String.Format("{0,0:F5}", all_params[i]); + output_clnf_params_file.Write("," + param); + } + output_clnf_params_file.WriteLine(); + } + + if (record_2D_landmarks) + { + List> landmarks_2d = clnf_model.CalculateLandmarks(); + + output_2D_landmarks_file.Write(String.Format("{0},{1},{2:F3}", frame_ind, success, confidence)); + + for (int i = 0; i < landmarks_2d.Count; ++i) + { + output_2D_landmarks_file.Write(",{0:F2}", landmarks_2d[i].Item1); + } + for (int i = 0; i < landmarks_2d.Count; ++i) + { + output_2D_landmarks_file.Write(",{0:F2}", landmarks_2d[i].Item2); + } + output_2D_landmarks_file.WriteLine(); + } + + if (record_3D_landmarks) + { + List landmarks_3d = clnf_model.Calculate3DLandmarks(fx, fy, cx, cy); + + output_3D_landmarks_file.Write(String.Format("{0},{1},{2:F3}", frame_ind, success, confidence)); + + for (int i = 0; i < landmarks_3d.Count; ++i) + { + output_3D_landmarks_file.Write(",{0:F2}", landmarks_3d[i].X); + } + for (int i = 0; i < landmarks_3d.Count; ++i) + { + output_3D_landmarks_file.Write(",{0:F2}", landmarks_3d[i].Y); + } + for (int i = 0; i < landmarks_3d.Count; ++i) + { + output_3D_landmarks_file.Write(",{0:F2}", landmarks_3d[i].Z); + } + output_3D_landmarks_file.WriteLine(); + } + + if (record_aus) + { + var au_classes = face_analyser.GetCurrentAUsClass(); + var au_regs = face_analyser.GetCurrentAUsReg(); + + output_au_class.Write(String.Format("{0},{1},{2:F3}", frame_ind, success, confidence)); + + foreach (var name_class in au_class_names) + { + output_au_class.Write(",{0:F0}", au_classes[name_class]); + } + output_au_class.WriteLine(); + + output_au_reg.Write(String.Format("{0},{1},{2:F3}", frame_ind, success, confidence)); + + foreach (var name_reg in au_reg_names) + { + output_au_reg.Write(",{0:F2}", au_regs[name_reg]); + } + output_au_reg.WriteLine(); + + } + + if (record_gaze) + { + + var gaze_cam = face_analyser.GetGazeCamera(); + + output_gaze.Write(String.Format("{0},{1},{2:F3}", frame_ind, success, confidence)); + + output_gaze.Write(String.Format(",{0:F3},{1:F3},{2:F3},{3:F3},{4:F3},{5:F3}", gaze_cam.Item1.Item1, gaze_cam.Item1.Item2, gaze_cam.Item1.Item3, + gaze_cam.Item2.Item1, gaze_cam.Item2.Item2, gaze_cam.Item2.Item3)); + + output_gaze.WriteLine(); + + } + + if (record_aligned) + { + face_analyser.RecordAlignedFrame(frame_ind); + } + + if (record_HOG) + { + face_analyser.RecordHOGFrame(); + } + + if (record_tracked_vid) + { + face_analyser.RecordTrackedFace(); + } + } + + // The main function call for processing images, video files or webcam feed + private void ProcessingLoop(String[] filenames, int cam_id = -1, int width = -1, int height = -1, bool multi_face = false) + { + + thread_running = true; + + mirror_image = false; + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + ResetButton.IsEnabled = true; + PauseButton.IsEnabled = true; + StopButton.IsEnabled = true; + })); + + // Create the video capture and call the VideoLoop + if (filenames != null) + { + clnf_params.optimiseForVideo(); + if (cam_id == -2) + { + List image_files_all = new List(); + foreach (string image_name in filenames) + image_files_all.Add(image_name); + + // Loading an image sequence that represents a video + capture = new Capture(image_files_all); + + if (capture.isOpened()) + { + // Prepare recording if any based on the directory + String file_no_ext = System.IO.Path.GetDirectoryName(filenames[0]); + file_no_ext = System.IO.Path.GetFileName(file_no_ext); + SetupRecording(record_root, file_no_ext, capture.width, capture.height); + + // Start the actual processing + VideoLoop(); + + // Clear up the recording + StopRecording(); + + } + else + { + string messageBoxText = "Failed to open an image"; + string caption = "Not valid file"; + MessageBoxButton button = MessageBoxButton.OK; + MessageBoxImage icon = MessageBoxImage.Warning; + + // Display message box + MessageBox.Show(messageBoxText, caption, button, icon); + } + } + else if (cam_id == -3) + { + SetupImageMode(); + clnf_params.optimiseForImages(); + // Loading an image file (or a number of them) + foreach (string filename in filenames) + { + if (!thread_running) + { + continue; + } + + capture = new Capture(filename); + + if (capture.isOpened()) + { + // Start the actual processing + ProcessImage(); + + } + else + { + string messageBoxText = "File is not an image or the decoder is not supported."; + string caption = "Not valid file"; + MessageBoxButton button = MessageBoxButton.OK; + MessageBoxImage icon = MessageBoxImage.Warning; + + // Display message box + MessageBox.Show(messageBoxText, caption, button, icon); + } + } + } + else + { + clnf_params.optimiseForVideo(); + // Loading a video file (or a number of them) + foreach (string filename in filenames) + { + if (!thread_running) + { + continue; + } + + capture = new Capture(filename); + + if (capture.isOpened()) + { + // Prepare recording if any + String file_no_ext = System.IO.Path.GetFileNameWithoutExtension(filename); + + SetupRecording(record_root, file_no_ext, capture.width, capture.height); + + // Start the actual processing + VideoLoop(); + + // Clear up the recording + StopRecording(); + } + else + { + string messageBoxText = "File is not a video or the codec is not supported."; + string caption = "Not valid file"; + MessageBoxButton button = MessageBoxButton.OK; + MessageBoxImage icon = MessageBoxImage.Warning; + + // Display message box + MessageBox.Show(messageBoxText, caption, button, icon); + } + } + } + } + else + { + capture = new Capture(cam_id, width, height); + mirror_image = true; + + if (capture.isOpened()) + { + // Prepare recording if any + String dir_out = DateTime.Now.ToString("yyyy-MMM-dd--HH-mm"); + + SetupRecording(record_root + "/" + dir_out, "webcam", width, height); + + // Start the actual processing + VideoLoop(); + + StopRecording(); + } + else + { + + string messageBoxText = "Failed to open a webcam"; + string caption = "Webcam failure"; + MessageBoxButton button = MessageBoxButton.OK; + MessageBoxImage icon = MessageBoxImage.Warning; + + // Display message box + MessageBox.Show(messageBoxText, caption, button, icon); + } + } + + // TODO this should be up a level + // Some GUI clean up + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + Console.WriteLine("Cleaning up after processing is done"); + PauseButton.IsEnabled = false; + StopButton.IsEnabled = false; + ResetButton.IsEnabled = false; + NextFiveFramesButton.IsEnabled = false; + NextFrameButton.IsEnabled = false; + })); + + } + + // Capturing and processing the video frame by frame + private void ProcessImage() + { + Thread.CurrentThread.IsBackground = true; + + clnf_model.Reset(); + face_analyser.Reset(); + + + ////////////////////////////////////////////// + // CAPTURE FRAME AND DETECT LANDMARKS FOLLOWED BY THE REQUIRED IMAGE PROCESSING + ////////////////////////////////////////////// + RawImage frame = null; + double progress = -1; + + frame = new RawImage(capture.GetNextFrame(mirror_image)); + progress = capture.GetProgress(); + + if (frame.Width == 0) + { + // This indicates that we reached the end of the video file + return; + } + + var grayFrame = new RawImage(capture.GetCurrentFrameGray()); + + if (grayFrame == null) + { + Console.WriteLine("Gray is empty"); + return; + } + + List>> landmark_detections = ProcessImage(clnf_model, clnf_params, frame, grayFrame); + + List landmark_points = new List(); + + for (int i = 0; i < landmark_detections.Count; ++i) + { + + List> landmarks = landmark_detections[i]; + foreach (var p in landmarks) + { + landmark_points.Add(new Point(p.Item1, p.Item2)); + } + } + + // Visualisation + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + if (show_tracked_video) + { + if (latest_img == null) + { + latest_img = frame.CreateWriteableBitmap(); + } + + frame.UpdateWriteableBitmap(latest_img); + + video.Source = latest_img; + video.Confidence = 1; + video.FPS = processing_fps.GetFPS(); + video.Progress = progress; + + video.OverlayLines = new List>(); + + video.OverlayPoints = landmark_points; + } + + })); + + latest_img = null; + } + + + // Capturing and processing the video frame by frame + private void VideoLoop() + { + Thread.CurrentThread.IsBackground = true; + + DateTime? startTime = CurrentTime; + + var lastFrameTime = CurrentTime; + + clnf_model.Reset(); + face_analyser.Reset(); + + // TODO add an ability to change these through a calibration procedure or setting menu + double fx, fy, cx, cy; + fx = 500.0; + fy = 500.0; + cx = cy = -1; + + int frame_id = 0; + + while (thread_running) + { + ////////////////////////////////////////////// + // CAPTURE FRAME AND DETECT LANDMARKS FOLLOWED BY THE REQUIRED IMAGE PROCESSING + ////////////////////////////////////////////// + RawImage frame = null; + double progress = -1; + + frame = new RawImage(capture.GetNextFrame(mirror_image)); + progress = capture.GetProgress(); + + if (frame.Width == 0) + { + // This indicates that we reached the end of the video file + break; + } + + // TODO stop button actually clears the video + + lastFrameTime = CurrentTime; + processing_fps.AddFrame(); + + var grayFrame = new RawImage(capture.GetCurrentFrameGray()); + + if (grayFrame == null) + { + Console.WriteLine("Gray is empty"); + continue; + } + + // This is more ore less guess work, but seems to work well enough + if (cx == -1) + { + fx = fx * (grayFrame.Width / 640.0); + fy = fy * (grayFrame.Height / 480.0); + + fx = (fx + fy) / 2.0; + fy = fx; + + cx = grayFrame.Width / 2f; + cy = grayFrame.Height / 2f; + } + + bool detectionSucceeding = ProcessFrame(clnf_model, clnf_params, frame, grayFrame, fx, fy, cx, cy); + + + + double confidence = (-clnf_model.GetConfidence()) / 2.0 + 0.5; + + if (confidence < 0) + confidence = 0; + else if (confidence > 1) + confidence = 1; + + List pose = new List(); + clnf_model.GetCorrectedPoseWorld(pose, fx, fy, cx, cy); + List non_rigid_params = clnf_model.GetNonRigidParams(); + + // The face analysis step (only done if recording AUs, HOGs or video) + if (record_aus || record_HOG || record_aligned || show_aus || show_appearance || record_tracked_vid || record_gaze) + { + face_analyser.AddNextFrame(frame, clnf_model, fx, fy, cx, cy, false, show_appearance, record_tracked_vid); + } + + List> lines = null; + List> landmarks = null; + List> gaze_lines = null; + + if (detectionSucceeding) + { + landmarks = clnf_model.CalculateLandmarks(); + lines = clnf_model.CalculateBox((float)fx, (float)fy, (float)cx, (float)cy); + gaze_lines = face_analyser.CalculateGazeLines((float)fx, (float)fy, (float)cx, (float)cy); + + } + + // Visualisation + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + if (show_aus) + { + var au_classes = face_analyser.GetCurrentAUsClass(); + var au_regs = face_analyser.GetCurrentAUsReg(); + + auClassGraph.Update(au_classes); + + var au_regs_scaled = new Dictionary(); + foreach (var au_reg in au_regs) + { + au_regs_scaled[au_reg.Key] = au_reg.Value / 5.0; + if (au_regs_scaled[au_reg.Key] < 0) + au_regs_scaled[au_reg.Key] = 0; + + if (au_regs_scaled[au_reg.Key] > 1) + au_regs_scaled[au_reg.Key] = 1; + } + auRegGraph.Update(au_regs_scaled); + } + + if (show_geometry) + { + int yaw = (int)(pose[4] * 180 / Math.PI + 0.5); + int roll = (int)(pose[5] * 180 / Math.PI + 0.5); + int pitch = (int)(pose[3] * 180 / Math.PI + 0.5); + + YawLabel.Content = yaw + "°"; + RollLabel.Content = roll + "°"; + PitchLabel.Content = pitch + "°"; + + XPoseLabel.Content = (int)pose[0] + " mm"; + YPoseLabel.Content = (int)pose[1] + " mm"; + ZPoseLabel.Content = (int)pose[2] + " mm"; + + nonRigidGraph.Update(non_rigid_params); + + // Update eye gaze + var gaze_both = face_analyser.GetGazeCamera(); + double x = (gaze_both.Item1.Item1 + gaze_both.Item2.Item1) / 2.0; + double y = (gaze_both.Item1.Item2 + gaze_both.Item2.Item2) / 2.0; + + // Tweak it to a more presentable value + x = (int)(x * 35); + y = (int)(y * 70); + + if (x < -10) + x = -10; + if (x > 10) + x = 10; + if (y < -10) + y = -10; + if (y > 10) + y = 10; + + GazeXLabel.Content = x / 10.0; + GazeYLabel.Content = y / 10.0; + + + } + + if (show_tracked_video) + { + if (latest_img == null) + { + latest_img = frame.CreateWriteableBitmap(); + } + + frame.UpdateWriteableBitmap(latest_img); + + video.Source = latest_img; + video.Confidence = confidence; + video.FPS = processing_fps.GetFPS(); + video.Progress = progress; + + if (!detectionSucceeding) + { + video.OverlayLines.Clear(); + video.OverlayPoints.Clear(); + video.GazeLines.Clear(); + } + else + { + video.OverlayLines = lines; + + List landmark_points = new List(); + foreach (var p in landmarks) + { + landmark_points.Add(new Point(p.Item1, p.Item2)); + } + + video.OverlayPoints = landmark_points; + + video.GazeLines = gaze_lines; + } + } + + if (show_appearance) + { + RawImage aligned_face = face_analyser.GetLatestAlignedFace(); + RawImage hog_face = face_analyser.GetLatestHOGDescriptorVisualisation(); + + if (latest_aligned_face == null) + { + latest_aligned_face = aligned_face.CreateWriteableBitmap(); + latest_HOG_descriptor = hog_face.CreateWriteableBitmap(); + } + + aligned_face.UpdateWriteableBitmap(latest_aligned_face); + hog_face.UpdateWriteableBitmap(latest_HOG_descriptor); + + AlignedFace.Source = latest_aligned_face; + AlignedHOG.Source = latest_HOG_descriptor; + } + })); + + // Recording the tracked model + RecordFrame(clnf_model, detectionSucceeding, frame_id, frame, grayFrame, fx, fy, cx, cy); + + if (reset) + { + clnf_model.Reset(); + face_analyser.Reset(); + reset = false; + } + + while (thread_running & thread_paused && skip_frames == 0) + { + Thread.Sleep(10); + } + + frame_id++; + + if (skip_frames > 0) + skip_frames--; + + } + + latest_img = null; + skip_frames = 0; + + // Unpause if it's paused + if (thread_paused) + { + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + PauseButton_Click(null, null); + })); + } + } + + private void StopTracking() + { + // First complete the running of the thread + if (processing_thread != null) + { + // Tell the other thread to finish + thread_running = false; + processing_thread.Join(); + } + } + + private void imageFileOpenClick(object sender, RoutedEventArgs e) + { + new Thread(() => imageOpen()).Start(); + } + + private void imageOpen() + { + StopTracking(); + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 2, 0), (Action)(() => + { + var d = new OpenFileDialog(); + d.Multiselect = true; + d.Filter = "Image files|*.jpg;*.jpeg;*.bmp;*.png;*.gif"; + + if (d.ShowDialog(this) == true) + { + + string[] image_files = d.FileNames; + + processing_thread = new Thread(() => ProcessingLoop(image_files, -3)); + processing_thread.Start(); + + } + })); + } + + private void videoFileOpenClick(object sender, RoutedEventArgs e) + { + new Thread(() => openVideoFile()).Start(); + } + + private void openVideoFile() + { + StopTracking(); + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 2, 0), (Action)(() => + { + var d = new OpenFileDialog(); + d.Multiselect = true; + d.Filter = "Video files|*.avi;*.wmv;*.mov;*.mpg;*.mpeg;*.mp4"; + + if (d.ShowDialog(this) == true) + { + + string[] video_files = d.FileNames; + + processing_thread = new Thread(() => ProcessingLoop(video_files)); + processing_thread.Start(); + + } + })); + } + + private void imageSequenceFileOpenClick(object sender, RoutedEventArgs e) + { + new Thread(() => imageSequenceOpen()).Start(); + } + + private void imageSequenceOpen() + { + StopTracking(); + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 2, 0), (Action)(() => + { + var d = new OpenFileDialog(); + d.Multiselect = true; + d.Filter = "Image files|*.jpg;*.jpeg;*.bmp;*.png;*.gif"; + + if (d.ShowDialog(this) == true) + { + + string[] image_files = d.FileNames; + + processing_thread = new Thread(() => ProcessingLoop(image_files, -2)); + processing_thread.Start(); + + } + })); + } + + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + if (processing_thread != null) + { + // Stop capture and tracking + thread_running = false; + processing_thread.Join(); + + capture.Dispose(); + } + face_analyser.Dispose(); + } + + // Stopping the tracking + private void StopButton_Click(object sender, RoutedEventArgs e) + { + if (processing_thread != null) + { + // Stop capture and tracking + thread_paused = false; + thread_running = false; + processing_thread.Join(); + + PauseButton.IsEnabled = false; + NextFrameButton.IsEnabled = false; + NextFiveFramesButton.IsEnabled = false; + StopButton.IsEnabled = false; + ResetButton.IsEnabled = false; + RecordingMenu.IsEnabled = true; + + UseDynamicModelsCheckBox.IsEnabled = true; + } + } + + // Stopping the tracking + private void ResetButton_Click(object sender, RoutedEventArgs e) + { + if (processing_thread != null) + { + // Stop capture and tracking + reset = true; + } + } + + // Stopping the tracking + private void PauseButton_Click(object sender, RoutedEventArgs e) + { + if (processing_thread != null) + { + // Stop capture and tracking + thread_paused = !thread_paused; + + ResetButton.IsEnabled = !thread_paused; + + NextFrameButton.IsEnabled = thread_paused; + NextFiveFramesButton.IsEnabled = thread_paused; + + if (thread_paused) + { + PauseButton.Content = "Resume"; + } + else + { + PauseButton.Content = "Pause"; + } + } + } + + private void SkipButton_Click(object sender, RoutedEventArgs e) + { + if (sender.Equals(NextFrameButton)) + { + skip_frames += 1; + } + else if (sender.Equals(NextFiveFramesButton)) + { + skip_frames += 5; + } + } + + private void VisualisationCheckBox_Click(object sender, RoutedEventArgs e) + { + show_tracked_video = ShowVideoCheckBox.IsChecked; + show_appearance = ShowAppearanceFeaturesCheckBox.IsChecked; + show_geometry = ShowGeometryFeaturesCheckBox.IsChecked; + show_aus = ShowAUsCheckBox.IsChecked; + + // Collapsing or restoring the windows here + if (!show_tracked_video) + { + VideoBorder.Visibility = System.Windows.Visibility.Collapsed; + MainGrid.ColumnDefinitions[0].Width = new GridLength(0, GridUnitType.Star); + } + else + { + VideoBorder.Visibility = System.Windows.Visibility.Visible; + MainGrid.ColumnDefinitions[0].Width = new GridLength(2.1, GridUnitType.Star); + } + + if (!show_appearance) + { + AppearanceBorder.Visibility = System.Windows.Visibility.Collapsed; + MainGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Star); + } + else + { + AppearanceBorder.Visibility = System.Windows.Visibility.Visible; + MainGrid.ColumnDefinitions[1].Width = new GridLength(0.8, GridUnitType.Star); + } + + // Collapsing or restoring the windows here + if (!show_geometry) + { + GeometryBorder.Visibility = System.Windows.Visibility.Collapsed; + MainGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Star); + } + else + { + GeometryBorder.Visibility = System.Windows.Visibility.Visible; + MainGrid.ColumnDefinitions[2].Width = new GridLength(1.0, GridUnitType.Star); + } + + // Collapsing or restoring the windows here + if (!show_aus) + { + ActionUnitBorder.Visibility = System.Windows.Visibility.Collapsed; + MainGrid.ColumnDefinitions[3].Width = new GridLength(0, GridUnitType.Star); + } + else + { + ActionUnitBorder.Visibility = System.Windows.Visibility.Visible; + MainGrid.ColumnDefinitions[3].Width = new GridLength(1.6, GridUnitType.Star); + } + + } + + private void SetupImageMode() + { + // Turn off recording + record_aus = false; + record_aligned = false; + record_HOG = false; + record_gaze = false; + record_tracked_vid = false; + record_2D_landmarks = false; + record_3D_landmarks = false; + record_params = false; + record_pose = false; + + // Turn off unneeded visualisations + show_tracked_video = true; + show_appearance = false; + show_geometry = false; + show_aus = false; + + // Actually update the GUI accordingly + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 2000), (Action)(() => + { + RecordAUCheckBox.IsChecked = record_aus; + RecordAlignedCheckBox.IsChecked = record_aligned; + RecordTrackedVidCheckBox.IsChecked = record_tracked_vid; + RecordHOGCheckBox.IsChecked = record_HOG; + RecordGazeCheckBox.IsChecked = record_gaze; + RecordLandmarks2DCheckBox.IsChecked = record_2D_landmarks; + RecordLandmarks3DCheckBox.IsChecked = record_3D_landmarks; + RecordParamsCheckBox.IsChecked = record_params; + RecordPoseCheckBox.IsChecked = record_pose; + + ShowVideoCheckBox.IsChecked = true; + ShowAppearanceFeaturesCheckBox.IsChecked = false; + ShowGeometryFeaturesCheckBox.IsChecked = false; + ShowAUsCheckBox.IsChecked = false; + + VisualisationCheckBox_Click(null, null); + })); + + // TODO change what next and back buttons do? + } + + private void recordCheckBox_click(object sender, RoutedEventArgs e) + { + record_aus = RecordAUCheckBox.IsChecked; + record_aligned = RecordAlignedCheckBox.IsChecked; + record_HOG = RecordHOGCheckBox.IsChecked; + record_gaze = RecordGazeCheckBox.IsChecked; + record_tracked_vid = RecordTrackedVidCheckBox.IsChecked; + record_2D_landmarks = RecordLandmarks2DCheckBox.IsChecked; + record_3D_landmarks = RecordLandmarks3DCheckBox.IsChecked; + record_params = RecordParamsCheckBox.IsChecked; + record_pose = RecordPoseCheckBox.IsChecked; + } + + private void UseDynamicModelsCheckBox_Click(object sender, RoutedEventArgs e) + { + dynamic_AU_shift = UseDynamicShiftingCheckBox.IsChecked; + dynamic_AU_scale = UseDynamicScalingCheckBox.IsChecked; + + if (use_dynamic_models != UseDynamicModelsCheckBox.IsChecked) + { + // Change the face analyser, this should be safe as the model is only allowed to change when not running + String root = AppDomain.CurrentDomain.BaseDirectory; + face_analyser = new FaceAnalyserManaged(root, UseDynamicModelsCheckBox.IsChecked); + } + use_dynamic_models = UseDynamicModelsCheckBox.IsChecked; + } + + } +} diff --git a/gui/OpenFace-offline/OpenFaceOffline.csproj b/gui/OpenFace-offline/OpenFaceOffline.csproj new file mode 100644 index 00000000..71f59c2f --- /dev/null +++ b/gui/OpenFace-offline/OpenFaceOffline.csproj @@ -0,0 +1,178 @@ + + + + + Debug + AnyCPU + {9661BE5C-2EE5-495E-BA64-6588602F411B} + WinExe + Properties + OpenFaceOffline + OpenFaceOffline + v4.5.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + + + + + + true + ..\..\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + ..\..\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + true + ..\..\x64\Debug\ + DEBUG;TRACE + full + AnyCPU + prompt + MinimumRecommendedRules.ruleset + true + + + ..\..\x64\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + BarGraph.xaml + + + + BarGraphHorizontal.xaml + + + MultiBarGraph.xaml + + + MultiBarGraphHorz.xaml + + + SimpleImage.xaml + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + OverlayImage.xaml + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + {78196985-ee54-411f-822b-5a23edf80642} + CppInerop + + + + + \ No newline at end of file diff --git a/gui/OpenFace-offline/Properties/AssemblyInfo.cs b/gui/OpenFace-offline/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..75449e41 --- /dev/null +++ b/gui/OpenFace-offline/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenFaceOffline")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenFaceOffline")] +[assembly: AssemblyCopyright("Copyright © Cambridge 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/gui/OpenFace-offline/Properties/Resources.Designer.cs b/gui/OpenFace-offline/Properties/Resources.Designer.cs new file mode 100644 index 00000000..a359eeae --- /dev/null +++ b/gui/OpenFace-offline/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace OpenFaceOffline.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenFaceOffline.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/gui/OpenFace-offline/Properties/Resources.resx b/gui/OpenFace-offline/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/gui/OpenFace-offline/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/gui/OpenFace-offline/Properties/Settings.Designer.cs b/gui/OpenFace-offline/Properties/Settings.Designer.cs new file mode 100644 index 00000000..f31bb9d0 --- /dev/null +++ b/gui/OpenFace-offline/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace OpenFaceOffline.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/gui/OpenFace-offline/Properties/Settings.settings b/gui/OpenFace-offline/Properties/Settings.settings new file mode 100644 index 00000000..033d7a5e --- /dev/null +++ b/gui/OpenFace-offline/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/gui/OpenFace-offline/UI_items/BarGraph.xaml b/gui/OpenFace-offline/UI_items/BarGraph.xaml new file mode 100644 index 00000000..2101d740 --- /dev/null +++ b/gui/OpenFace-offline/UI_items/BarGraph.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/gui/OpenFace-offline/UI_items/BarGraph.xaml.cs b/gui/OpenFace-offline/UI_items/BarGraph.xaml.cs new file mode 100644 index 00000000..0a089c76 --- /dev/null +++ b/gui/OpenFace-offline/UI_items/BarGraph.xaml.cs @@ -0,0 +1,39 @@ +using System.Windows.Controls; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for BarGraph.xaml + /// + public partial class BarGraph : UserControl + { + private double targetValue = 0; + + public BarGraph() + { + InitializeComponent(); + } + + public void SetValue(double value) + { + targetValue = 1.5 * value; + if (targetValue > 0) + { + if (targetValue > barContainerPos.ActualHeight) + targetValue = barContainerPos.ActualHeight; + + barPos.Height = targetValue; + barNeg.Height = 0; + } + if (targetValue < 0) + { + if (-targetValue > barContainerNeg.ActualHeight) + targetValue = -barContainerNeg.ActualHeight; + + barPos.Height = 0; + barNeg.Height = -targetValue; + } + } + + } +} diff --git a/gui/OpenFace-offline/UI_items/BarGraphHorizontal.xaml b/gui/OpenFace-offline/UI_items/BarGraphHorizontal.xaml new file mode 100644 index 00000000..f5ccbe5e --- /dev/null +++ b/gui/OpenFace-offline/UI_items/BarGraphHorizontal.xaml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/gui/OpenFace-offline/UI_items/BarGraphHorizontal.xaml.cs b/gui/OpenFace-offline/UI_items/BarGraphHorizontal.xaml.cs new file mode 100644 index 00000000..8c18d4a0 --- /dev/null +++ b/gui/OpenFace-offline/UI_items/BarGraphHorizontal.xaml.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for BarGraphHorizontal.xaml + /// + public partial class BarGraphHorizontal : UserControl + { + double targetValue = 0; + + public BarGraphHorizontal(String label) + { + InitializeComponent(); + Label.Content = label; + } + + public void SetValue(double value) + { + targetValue = value; + barPos.Width = targetValue * barContainerPos.ActualWidth; + } + + public double GetTarget() + { + return targetValue; + } + } +} diff --git a/gui/OpenFace-offline/UI_items/MultiBarGraph.xaml b/gui/OpenFace-offline/UI_items/MultiBarGraph.xaml new file mode 100644 index 00000000..836c9742 --- /dev/null +++ b/gui/OpenFace-offline/UI_items/MultiBarGraph.xaml @@ -0,0 +1,11 @@ + + + + + diff --git a/gui/OpenFace-offline/UI_items/MultiBarGraph.xaml.cs b/gui/OpenFace-offline/UI_items/MultiBarGraph.xaml.cs new file mode 100644 index 00000000..c1019417 --- /dev/null +++ b/gui/OpenFace-offline/UI_items/MultiBarGraph.xaml.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for MultiBarGraph.xaml + /// + public partial class MultiBarGraph : UserControl + { + + int num_bars = 0; + List graphs; + + public MultiBarGraph() + { + InitializeComponent(); + + graphs = new List(); + } + + public void Update(List data) + { + // Create new bars if necessary + if (num_bars != data.Count) + { + num_bars = data.Count; + barGrid.Children.Clear(); + foreach (var value in data) + { + BarGraph newBar = new BarGraph(); + graphs.Add(newBar); + barGrid.ColumnDefinitions.Add(new ColumnDefinition()); + Grid.SetColumn(newBar, graphs.Count); + barGrid.Children.Add(newBar); + + } + } + + // Update the bars + for (int i = 0; i < data.Count; ++i) + { + graphs[i].SetValue(data[i]); + } + } + } +} diff --git a/gui/OpenFace-offline/UI_items/MultiBarGraphHorz.xaml b/gui/OpenFace-offline/UI_items/MultiBarGraphHorz.xaml new file mode 100644 index 00000000..db5728f6 --- /dev/null +++ b/gui/OpenFace-offline/UI_items/MultiBarGraphHorz.xaml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/gui/OpenFace-offline/UI_items/MultiBarGraphHorz.xaml.cs b/gui/OpenFace-offline/UI_items/MultiBarGraphHorz.xaml.cs new file mode 100644 index 00000000..f960b71e --- /dev/null +++ b/gui/OpenFace-offline/UI_items/MultiBarGraphHorz.xaml.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for MultiBarGraphHorz.xaml + /// + public partial class MultiBarGraphHorz : UserControl + { + int num_bars = 0; + Dictionary graphs; + + // Name mapping + Dictionary mapping; + + public MultiBarGraphHorz() + { + InitializeComponent(); + + graphs = new Dictionary(); + + mapping = new Dictionary(); + mapping["AU01"] = "Inner Brow raiser"; + mapping["AU02"] = "Outer Brow raiser"; + mapping["AU04"] = "Brow lowerer"; + mapping["AU05"] = "Upper lid raiser"; + mapping["AU06"] = "Cheek raiser"; + mapping["AU07"] = "Lid tightener"; + mapping["AU09"] = "Nose wrinkler"; + mapping["AU10"] = "Upper lip raiser"; + mapping["AU12"] = "Lip corner puller (smile)"; + mapping["AU14"] = "Dimpler"; + mapping["AU15"] = "Lip corner depressor"; + mapping["AU17"] = "Chin Raiser"; + mapping["AU20"] = "Lip Stretcher"; + mapping["AU23"] = "Lip tightener"; + mapping["AU25"] = "Lips part"; + mapping["AU26"] = "Jaw drop"; + mapping["AU28"] = "Lip suck"; + mapping["AU45"] = "Blink"; + + + + } + + public void Update(Dictionary data) + { + // Create new bars if necessary + if (num_bars != data.Count) + { + num_bars = data.Count; + barGrid.Children.Clear(); + + // Make sure AUs are sorted + var data_labels = data.Keys.ToList(); + data_labels.Sort(); + + foreach (var label in data_labels) + { + BarGraphHorizontal newBar = new BarGraphHorizontal(label + " - " + mapping[label]); + barGrid.RowDefinitions.Add(new RowDefinition()); + Grid.SetRow(newBar, graphs.Count); + graphs.Add(label, newBar); + barGrid.Children.Add(newBar); + } + } + + // Update the bars + foreach (var value in data) + { + double old_value = graphs[value.Key].GetTarget(); + // some smoothing as well + graphs[value.Key].SetValue(old_value * 0.15 + 0.85 * value.Value); + } + } + } +} diff --git a/gui/OpenFace-offline/UI_items/OverlayImage.xaml b/gui/OpenFace-offline/UI_items/OverlayImage.xaml new file mode 100644 index 00000000..8dcdaea5 --- /dev/null +++ b/gui/OpenFace-offline/UI_items/OverlayImage.xaml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/gui/OpenFace-offline/UI_items/OverlayImage.xaml.cs b/gui/OpenFace-offline/UI_items/OverlayImage.xaml.cs new file mode 100644 index 00000000..4546fec2 --- /dev/null +++ b/gui/OpenFace-offline/UI_items/OverlayImage.xaml.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for OverlayImage.xaml + /// + public partial class OverlayImage : Image + { + public OverlayImage() + { + InitializeComponent(); + OverlayLines = new List>(); + OverlayPoints = new List(); + GazeLines = new List>(); + + Progress = -1; + } + + protected override void OnRender(DrawingContext dc) + { + base.OnRender(dc); + + if (OverlayLines == null) + OverlayLines = new List>(); + + if (OverlayPoints == null) + OverlayPoints = new List(); + + if (Source == null || !(Source is WriteableBitmap)) + return; + + var width = ((WriteableBitmap)Source).PixelWidth; + var height = ((WriteableBitmap)Source).PixelHeight; + + foreach (var line in OverlayLines) + { + + var p1 = new Point(ActualWidth * line.Item1.X / width, ActualHeight * line.Item1.Y / height); + var p2 = new Point(ActualWidth * line.Item2.X / width, ActualHeight * line.Item2.Y / height); + + dc.DrawLine(new Pen(new SolidColorBrush(Color.FromArgb(200, (byte)(100 + (155 * (1 - Confidence))), (byte)(100 + (155 * Confidence)), 100)), 2), p1, p2); + } + + foreach (var line in GazeLines) + { + + var p1 = new Point(ActualWidth * line.Item1.X / width, ActualHeight * line.Item1.Y / height); + var p2 = new Point(ActualWidth * line.Item2.X / width, ActualHeight * line.Item2.Y / height); + + dc.DrawLine(new Pen(new SolidColorBrush(Color.FromArgb(200, (byte)(240), (byte)(30), (byte)100)), 3), p1, p2); + + } + + foreach (var p in OverlayPoints) + { + + var q = new Point(ActualWidth * p.X / width, ActualHeight * p.Y / height); + + dc.DrawEllipse(new SolidColorBrush(Color.FromArgb((byte)(200 * Confidence), 255, 255, 100)), null, q, 2, 2); + } + + double scaling = ActualWidth / 400.0; + + int confidence_width = (int)(107.0 * scaling); + int confidence_height = (int)(18.0 * scaling); + + Brush conf_brush = new SolidColorBrush(Color.FromRgb((byte)((1 - Confidence) * 255), (byte)(Confidence * 255), (byte)40)); + dc.DrawRoundedRectangle(conf_brush, new Pen(Brushes.Black, 0.5 * scaling), new Rect(ActualWidth - confidence_width - 1, 0, confidence_width, confidence_height), 3.0 * scaling, 3.0 * scaling); + + FormattedText txt = new FormattedText("Confidence: " + (int)(100 * Confidence) + "%", System.Globalization.CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, new Typeface("Verdana"), 12.0 * scaling, Brushes.Black); + dc.DrawText(txt, new Point(ActualWidth - confidence_width + 2, 2)); + + int fps_width = (int)(52.0 * scaling); + int fps_height = (int)(18.0 * scaling); + + dc.DrawRoundedRectangle(Brushes.WhiteSmoke, new Pen(Brushes.Black, 0.5 * scaling), new Rect(0, 0, fps_width, fps_height), 3.0 * scaling, 3.0 * scaling); + FormattedText fps_txt = new FormattedText("FPS: " + (int)FPS, System.Globalization.CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, new Typeface("Verdana"), 12.0 * scaling, Brushes.Black); + dc.DrawText(fps_txt, new Point(2.0 * scaling, 0)); + + old_width = width; + old_height = height; + + // Drawing a progress bar at the bottom of the image + if (Progress > 0) + { + int progress_bar_height = (int)(6.0 * scaling); + dc.DrawRectangle(Brushes.GreenYellow, new Pen(Brushes.Black, 0.5 * scaling), new Rect(0, ActualHeight - progress_bar_height, Progress * ActualWidth, progress_bar_height)); + } + + } + + public List> OverlayLines { get; set; } + public List> GazeLines { get; set; } + public List OverlayPoints { get; set; } + public double Confidence { get; set; } + public double FPS { get; set; } + + // 0 to 1 indicates how much video has been processed so far + public double Progress { get; set; } + + int old_width; + int old_height; + } +} diff --git a/gui/OpenFace-offline/UI_items/SimpleImage.xaml b/gui/OpenFace-offline/UI_items/SimpleImage.xaml new file mode 100644 index 00000000..9cf612d7 --- /dev/null +++ b/gui/OpenFace-offline/UI_items/SimpleImage.xaml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/gui/OpenFace-offline/UI_items/SimpleImage.xaml.cs b/gui/OpenFace-offline/UI_items/SimpleImage.xaml.cs new file mode 100644 index 00000000..7ef9830e --- /dev/null +++ b/gui/OpenFace-offline/UI_items/SimpleImage.xaml.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for OverlayImage.xaml + /// + public partial class SimpleImage : Image + { + public SimpleImage() + { + InitializeComponent(); + } + + protected override void OnRender(DrawingContext dc) + { + base.OnRender(dc); + + if (Source == null || !(Source is WriteableBitmap)) + return; + } + } +} diff --git a/gui/OpenFaceOffline/App.config b/gui/OpenFaceOffline/App.config new file mode 100644 index 00000000..88fa4027 --- /dev/null +++ b/gui/OpenFaceOffline/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/gui/OpenFaceOffline/App.xaml b/gui/OpenFaceOffline/App.xaml new file mode 100644 index 00000000..a7c9cabe --- /dev/null +++ b/gui/OpenFaceOffline/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/gui/OpenFaceOffline/App.xaml.cs b/gui/OpenFaceOffline/App.xaml.cs new file mode 100644 index 00000000..300701a5 --- /dev/null +++ b/gui/OpenFaceOffline/App.xaml.cs @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/gui/OpenFaceOffline/FpsTracker.cs b/gui/OpenFaceOffline/FpsTracker.cs new file mode 100644 index 00000000..6bb59080 --- /dev/null +++ b/gui/OpenFaceOffline/FpsTracker.cs @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; + +namespace OpenFaceOffline +{ + public class FpsTracker + { + public TimeSpan HistoryLength { get; set; } + public FpsTracker() + { + HistoryLength = TimeSpan.FromSeconds(2); + } + + private Queue frameTimes = new Queue(); + + private void DiscardOldFrames() + { + while (frameTimes.Count > 0 && (MainWindow.CurrentTime - frameTimes.Peek()) > HistoryLength) + frameTimes.Dequeue(); + } + + public void AddFrame() + { + frameTimes.Enqueue(MainWindow.CurrentTime); + DiscardOldFrames(); + } + + public double GetFPS() + { + DiscardOldFrames(); + + if (frameTimes.Count == 0) + return 0; + + return frameTimes.Count / (MainWindow.CurrentTime - frameTimes.Peek()).TotalSeconds; + } + } +} diff --git a/gui/OpenFaceOffline/MainWindow.xaml b/gui/OpenFaceOffline/MainWindow.xaml new file mode 100644 index 00000000..90499553 --- /dev/null +++ b/gui/OpenFaceOffline/MainWindow.xaml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Appearance features + + + + + + + + + + + + + + + + + + + + + Geometry features + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Action Units + + + + + Classification + + + + + + + Regression + + + + + + + + + + + + + + + diff --git a/gui/OpenFaceOffline/MainWindow.xaml.cs b/gui/OpenFaceOffline/MainWindow.xaml.cs new file mode 100644 index 00000000..295d740c --- /dev/null +++ b/gui/OpenFaceOffline/MainWindow.xaml.cs @@ -0,0 +1,1194 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Windows; +using System.Windows.Threading; +using System.Windows.Media.Imaging; +using System.IO; +using Microsoft.Win32; + +// Internal libraries +using OpenCVWrappers; +using CppInterop; +using CppInterop.LandmarkDetector; +using CameraInterop; +using FaceAnalyser_Interop; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + + // Timing for measuring FPS + #region High-Resolution Timing + static DateTime startTime; + static Stopwatch sw = new Stopwatch(); + + static MainWindow() + { + startTime = DateTime.Now; + sw.Start(); + } + + public static DateTime CurrentTime + { + get { return startTime + sw.Elapsed; } + } + #endregion + + // ----------------------------------------------------------------- + // Members + // ----------------------------------------------------------------- + + Thread processing_thread; + + // Some members for displaying the results + private Capture capture; + private WriteableBitmap latest_img; + private WriteableBitmap latest_aligned_face; + private WriteableBitmap latest_HOG_descriptor; + + // Managing the running of the analysis system + private volatile bool thread_running; + private volatile bool thread_paused = false; + // Allows for going forward in time step by step + // Useful for visualising things + private volatile int skip_frames = 0; + + FpsTracker processing_fps = new FpsTracker(); + + volatile bool detectionSucceeding = false; + + volatile bool reset = false; + + // For tracking + FaceModelParameters clnf_params; + CLNF clnf_model; + FaceAnalyserManaged face_analyser; + + // Recording parameters (default values) + // TODO these should only be initialized when starting the recording, might not need to have them as members (also all should be on by default) + bool record_HOG = false; // HOG features extracted from face images + bool record_aligned = false; // aligned face images + bool record_tracked_vid = false; + + // Visualisation options + bool show_tracked_video = true; + bool show_appearance = true; + bool show_geometry = true; + bool show_aus = true; + + // TODO classifiers converted to regressors + + // TODO indication that track is done + + // The recording managers, TODO they should be all one + StreamWriter output_features_file; + + // Where the recording is done (by default in a record directory, from where the application executed), TODO maybe the same folder as iput? + String record_root = "./record"; + + // For AU visualisation and output + List au_class_names; + List au_reg_names; + + // For AU prediction + bool dynamic_AU_shift = true; + bool dynamic_AU_scale = false; + bool use_dynamic_models = true; + + public MainWindow() + { + InitializeComponent(); + + // Set the icon + Uri iconUri = new Uri("logo1.ico", UriKind.RelativeOrAbsolute); + this.Icon = BitmapFrame.Create(iconUri); + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 2000), (Action)(() => + { + RecordAlignedCheckBox.IsChecked = record_aligned; + RecordTrackedVidCheckBox.IsChecked = record_tracked_vid; + RecordHOGCheckBox.IsChecked = record_HOG; + + UseDynamicModelsCheckBox.IsChecked = use_dynamic_models; + UseDynamicScalingCheckBox.IsChecked = dynamic_AU_scale; + UseDynamicShiftingCheckBox.IsChecked = dynamic_AU_shift; + })); + + String root = AppDomain.CurrentDomain.BaseDirectory; + + clnf_params = new FaceModelParameters(root); + clnf_model = new CLNF(clnf_params); + face_analyser = new FaceAnalyserManaged(root, use_dynamic_models); + + } + + // ---------------------------------------------------------- + // Actual work gets done here + + // The main function call for processing images or video files + private void ProcessingLoop(String[] filenames, int cam_id = -1, int width = -1, int height = -1, bool multi_face = false) + { + + thread_running = true; + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + ResetButton.IsEnabled = true; + PauseButton.IsEnabled = true; + StopButton.IsEnabled = true; + })); + + // Create the video capture and call the VideoLoop + if (filenames != null) + { + clnf_params.optimiseForVideo(); + if (cam_id == -2) + { + List image_files_all = new List(); + foreach (string image_name in filenames) + image_files_all.Add(image_name); + + // Loading an image sequence that represents a video + capture = new Capture(image_files_all); + + if (capture.isOpened()) + { + // Prepare recording if any based on the directory + String file_no_ext = System.IO.Path.GetDirectoryName(filenames[0]); + file_no_ext = System.IO.Path.GetFileName(file_no_ext); + SetupRecording(record_root, file_no_ext, capture.width, capture.height); + + // Start the actual processing + VideoLoop(); + + // Clear up the recording + StopRecording(); + + } + else + { + string messageBoxText = "Failed to open an image"; + string caption = "Not valid file"; + MessageBoxButton button = MessageBoxButton.OK; + MessageBoxImage icon = MessageBoxImage.Warning; + + // Display message box + MessageBox.Show(messageBoxText, caption, button, icon); + } + } + else if (cam_id == -3) + { + SetupImageMode(); + clnf_params.optimiseForImages(); + // Loading an image file (or a number of them) + foreach (string filename in filenames) + { + if (!thread_running) + { + continue; + } + + capture = new Capture(filename); + + if (capture.isOpened()) + { + // Start the actual processing + ProcessImage(); + + } + else + { + string messageBoxText = "File is not an image or the decoder is not supported."; + string caption = "Not valid file"; + MessageBoxButton button = MessageBoxButton.OK; + MessageBoxImage icon = MessageBoxImage.Warning; + + // Display message box + MessageBox.Show(messageBoxText, caption, button, icon); + } + } + } + else + { + clnf_params.optimiseForVideo(); + // Loading a video file (or a number of them) + foreach (string filename in filenames) + { + if (!thread_running) + { + continue; + } + + capture = new Capture(filename); + + if (capture.isOpened()) + { + // Prepare recording if any + String file_no_ext = System.IO.Path.GetFileNameWithoutExtension(filename); + + SetupRecording(record_root, file_no_ext, capture.width, capture.height); + + // Start the actual processing + VideoLoop(); + + // Clear up the recording + StopRecording(); + } + else + { + string messageBoxText = "File is not a video or the codec is not supported."; + string caption = "Not valid file"; + MessageBoxButton button = MessageBoxButton.OK; + MessageBoxImage icon = MessageBoxImage.Warning; + + // Display message box + MessageBox.Show(messageBoxText, caption, button, icon); + } + } + } + } + + // TODO this should be up a level + // Some GUI clean up + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + Console.WriteLine("Cleaning up after processing is done"); + PauseButton.IsEnabled = false; + StopButton.IsEnabled = false; + ResetButton.IsEnabled = false; + NextFiveFramesButton.IsEnabled = false; + NextFrameButton.IsEnabled = false; + })); + + } + + // Capturing and processing the video frame by frame + private void ProcessImage() + { + Thread.CurrentThread.IsBackground = true; + + clnf_model.Reset(); + face_analyser.Reset(); + + + ////////////////////////////////////////////// + // CAPTURE FRAME AND DETECT LANDMARKS FOLLOWED BY THE REQUIRED IMAGE PROCESSING + ////////////////////////////////////////////// + RawImage frame = null; + double progress = -1; + + frame = new RawImage(capture.GetNextFrame(false)); + progress = capture.GetProgress(); + + if (frame.Width == 0) + { + // This indicates that we reached the end of the video file + return; + } + + var grayFrame = new RawImage(capture.GetCurrentFrameGray()); + + if (grayFrame == null) + { + Console.WriteLine("Gray is empty"); + return; + } + + List>> landmark_detections = ProcessImage(clnf_model, clnf_params, frame, grayFrame); + + List landmark_points = new List(); + + for (int i = 0; i < landmark_detections.Count; ++i) + { + + List> landmarks = landmark_detections[i]; + foreach (var p in landmarks) + { + landmark_points.Add(new Point(p.Item1, p.Item2)); + } + } + + // Visualisation + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + if (show_tracked_video) + { + if (latest_img == null) + { + latest_img = frame.CreateWriteableBitmap(); + } + + frame.UpdateWriteableBitmap(latest_img); + + video.Source = latest_img; + video.Confidence = 1; + video.FPS = processing_fps.GetFPS(); + video.Progress = progress; + + video.OverlayLines = new List>(); + + video.OverlayPoints = landmark_points; + } + + })); + + latest_img = null; + } + + + // Capturing and processing the video frame by frame + private void VideoLoop() + { + Thread.CurrentThread.IsBackground = true; + + DateTime? startTime = CurrentTime; + + var lastFrameTime = CurrentTime; + + clnf_model.Reset(); + face_analyser.Reset(); + + // TODO add an ability to change these through a calibration procedure or setting menu + double fx, fy, cx, cy; + fx = 500.0; + fy = 500.0; + cx = cy = -1; + + int frame_id = 0; + + double fps = capture.GetFPS(); + if (fps <= 0) fps = 30; + + // Check wich things need to be recorded + bool output_2D_landmarks = RecordLandmarks2DCheckBox.IsChecked; + bool output_3D_landmarks = RecordLandmarks3DCheckBox.IsChecked; + bool output_model_params = RecordParamsCheckBox.IsChecked; + bool output_pose = RecordPoseCheckBox.IsChecked; + bool output_AUs = RecordAUCheckBox.IsChecked; + bool output_gaze = RecordGazeCheckBox.IsChecked; + + while (thread_running) + { + ////////////////////////////////////////////// + // CAPTURE FRAME AND DETECT LANDMARKS FOLLOWED BY THE REQUIRED IMAGE PROCESSING + ////////////////////////////////////////////// + RawImage frame = null; + double progress = -1; + + frame = new RawImage(capture.GetNextFrame(false)); + progress = capture.GetProgress(); + + if (frame.Width == 0) + { + // This indicates that we reached the end of the video file + break; + } + + // TODO stop button should actually clear the video + lastFrameTime = CurrentTime; + processing_fps.AddFrame(); + + var grayFrame = new RawImage(capture.GetCurrentFrameGray()); + + if (grayFrame == null) + { + Console.WriteLine("Gray is empty"); + continue; + } + + // This is more ore less guess work, but seems to work well enough + if (cx == -1) + { + fx = fx * (grayFrame.Width / 640.0); + fy = fy * (grayFrame.Height / 480.0); + + fx = (fx + fy) / 2.0; + fy = fx; + + cx = grayFrame.Width / 2f; + cy = grayFrame.Height / 2f; + } + + bool detectionSucceeding = ProcessFrame(clnf_model, clnf_params, frame, grayFrame, fx, fy, cx, cy); + + double confidence = (-clnf_model.GetConfidence()) / 2.0 + 0.5; + + if (confidence < 0) + confidence = 0; + else if (confidence > 1) + confidence = 1; + + List pose = new List(); + clnf_model.GetCorrectedPoseWorld(pose, fx, fy, cx, cy); + List non_rigid_params = clnf_model.GetNonRigidParams(); + + // The face analysis step (only done if recording AUs, HOGs or video) + if (output_AUs || record_HOG || record_aligned || show_aus || show_appearance || record_tracked_vid || output_gaze) + { + face_analyser.AddNextFrame(frame, clnf_model, fx, fy, cx, cy, false, show_appearance, record_tracked_vid); + } + + List> lines = null; + List> landmarks = null; + List> gaze_lines = null; + + if (detectionSucceeding) + { + landmarks = clnf_model.CalculateLandmarks(); + lines = clnf_model.CalculateBox((float)fx, (float)fy, (float)cx, (float)cy); + //gaze_lines = face_analyser.CalculateGazeLines((float)fx, (float)fy, (float)cx, (float)cy); // TODO figure out what is happening here + gaze_lines = new List>(); + } + + // Visualisation + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + if (show_aus) + { + var au_classes = face_analyser.GetCurrentAUsClass(); + var au_regs = face_analyser.GetCurrentAUsReg(); + + auClassGraph.Update(au_classes); + + var au_regs_scaled = new Dictionary(); + foreach (var au_reg in au_regs) + { + au_regs_scaled[au_reg.Key] = au_reg.Value / 5.0; + if (au_regs_scaled[au_reg.Key] < 0) + au_regs_scaled[au_reg.Key] = 0; + + if (au_regs_scaled[au_reg.Key] > 1) + au_regs_scaled[au_reg.Key] = 1; + } + auRegGraph.Update(au_regs_scaled); + } + + if (show_geometry) + { + int yaw = (int)(pose[4] * 180 / Math.PI + 0.5); + int roll = (int)(pose[5] * 180 / Math.PI + 0.5); + int pitch = (int)(pose[3] * 180 / Math.PI + 0.5); + + YawLabel.Content = yaw + "°"; + RollLabel.Content = roll + "°"; + PitchLabel.Content = pitch + "°"; + + XPoseLabel.Content = (int)pose[0] + " mm"; + YPoseLabel.Content = (int)pose[1] + " mm"; + ZPoseLabel.Content = (int)pose[2] + " mm"; + + nonRigidGraph.Update(non_rigid_params); + + // Update eye gaze + var gaze_both = face_analyser.GetGazeCamera(); + double x = (gaze_both.Item1.Item1 + gaze_both.Item2.Item1) / 2.0; + double y = (gaze_both.Item1.Item2 + gaze_both.Item2.Item2) / 2.0; + + // Tweak it to a more presentable value + x = (int)(x * 35); + y = (int)(y * 70); + + if (x < -10) + x = -10; + if (x > 10) + x = 10; + if (y < -10) + y = -10; + if (y > 10) + y = 10; + + GazeXLabel.Content = x / 10.0; + GazeYLabel.Content = y / 10.0; + + + } + + if (show_tracked_video) + { + if (latest_img == null) + { + latest_img = frame.CreateWriteableBitmap(); + } + + frame.UpdateWriteableBitmap(latest_img); + + video.Source = latest_img; + video.Confidence = confidence; + video.FPS = processing_fps.GetFPS(); + video.Progress = progress; + + if (!detectionSucceeding) + { + video.OverlayLines.Clear(); + video.OverlayPoints.Clear(); + video.GazeLines.Clear(); + } + else + { + video.OverlayLines = lines; + + List landmark_points = new List(); + foreach (var p in landmarks) + { + landmark_points.Add(new Point(p.Item1, p.Item2)); + } + + video.OverlayPoints = landmark_points; + + video.GazeLines = gaze_lines; + } + } + + if (show_appearance) + { + RawImage aligned_face = face_analyser.GetLatestAlignedFace(); + RawImage hog_face = face_analyser.GetLatestHOGDescriptorVisualisation(); + + if (latest_aligned_face == null) + { + latest_aligned_face = aligned_face.CreateWriteableBitmap(); + latest_HOG_descriptor = hog_face.CreateWriteableBitmap(); + } + + aligned_face.UpdateWriteableBitmap(latest_aligned_face); + hog_face.UpdateWriteableBitmap(latest_HOG_descriptor); + + AlignedFace.Source = latest_aligned_face; + AlignedHOG.Source = latest_HOG_descriptor; + } + })); + + // Recording the tracked model + RecordFrame(clnf_model, detectionSucceeding, frame_id, frame, grayFrame, (fps * (double)frame_id)/1000.0, + output_2D_landmarks, output_2D_landmarks, output_model_params, output_pose, output_AUs, output_gaze, fx, fy, cx, cy); + + if (reset) + { + clnf_model.Reset(); + face_analyser.Reset(); + reset = false; + } + + while (thread_running & thread_paused && skip_frames == 0) + { + Thread.Sleep(10); + } + + frame_id++; + + if (skip_frames > 0) + skip_frames--; + + } + + latest_img = null; + skip_frames = 0; + + // Unpause if it's paused + if (thread_paused) + { + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + PauseButton_Click(null, null); + })); + } + } + + private void StopTracking() + { + // First complete the running of the thread + if (processing_thread != null) + { + // Tell the other thread to finish + thread_running = false; + processing_thread.Join(); + } + } + + + + // ---------------------------------------------------------- + // Interacting with landmark detection and face analysis + + private bool ProcessFrame(CLNF clnf_model, FaceModelParameters clnf_params, RawImage frame, RawImage grayscale_frame, double fx, double fy, double cx, double cy) + { + detectionSucceeding = clnf_model.DetectLandmarksInVideo(grayscale_frame, clnf_params); + return detectionSucceeding; + + } + + private List>> ProcessImage(CLNF clnf_model, FaceModelParameters clnf_params, RawImage frame, RawImage grayscale_frame) + { + List>> landmark_detections = clnf_model.DetectMultiFaceLandmarksInImage(grayscale_frame, clnf_params); + return landmark_detections; + + } + + + // ---------------------------------------------------------- + // Recording helpers (TODO simplify) + + private void SetupRecording(String root, String filename, int width, int height, bool output_2D_landmarks, bool output_3D_landmarks, + bool output_model_params, bool output_pose, bool output_AUs, bool output_gaze) + { + // Disallow changing recording settings when the recording starts, TODO move this up a bit + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + RecordingMenu.IsEnabled = false; + UseDynamicModelsCheckBox.IsEnabled = false; + })); + + if (!System.IO.Directory.Exists(root)) + { + System.IO.Directory.CreateDirectory(root); + } + + output_features_file = new StreamWriter(root + "/" + filename + ".txt"); + output_features_file.Write("frame, timestamp, confidence, success"); + + if (output_gaze) + output_features_file.Write(", gaze_0_x, gaze_0_y, gaze_0_z, gaze_1_x, gaze_1_y, gaze_2_z"); + + if (output_pose) + output_features_file.Write(", pose_Tx, pose_Ty, pose_Tz, pose_Rx, pose_Ry, pose_Rz"); + + if (output_2D_landmarks) + { + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_features_file.Write(", x_" + i); + } + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_features_file.Write(", y_" + i); + } + } + + if (output_3D_landmarks) + { + + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_features_file.Write(", X_" + i); + } + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_features_file.Write(", Y_" + i); + } + for (int i = 0; i < clnf_model.GetNumPoints(); ++i) + { + output_features_file.Write(", Z_" + i); + } + } + + if (output_model_params) + { + output_features_file.Write(", p_scale, p_rx, p_ry, p_rz, p_tx, p_ty"); + for (int i = 0; i < clnf_model.GetNumModes(); ++i) + { + output_features_file.Write(", p_" + i); + } + } + + if (output_AUs) + { + + au_reg_names = face_analyser.GetRegActionUnitsNames(); + au_reg_names.Sort(); + foreach (var name in au_reg_names) + { + output_features_file.Write(", " + name + "_r"); + } + + au_class_names = face_analyser.GetClassActionUnitsNames(); + au_class_names.Sort(); + foreach (var name in au_class_names) + { + output_features_file.Write(", " + name + "_c"); + } + + } + + output_features_file.WriteLine(); + + + if (record_aligned) + { + String aligned_root = root + "/" + filename + "_aligned/"; + System.IO.Directory.CreateDirectory(aligned_root); + face_analyser.SetupAlignedImageRecording(aligned_root); + } + + if (record_tracked_vid) + { + String vid_loc = root + "/" + filename + ".avi"; + System.IO.Directory.CreateDirectory(root); + face_analyser.SetupTrackingRecording(vid_loc, width, height, 30); + } + + if (record_HOG) + { + String filename_HOG = root + "/" + filename + ".hog"; + face_analyser.SetupHOGRecording(filename_HOG); + } + + } + + private void StopRecording() + { + if (output_features_file != null) + output_features_file.Close(); + + if (record_HOG) + face_analyser.StopHOGRecording(); + + if (record_tracked_vid) + face_analyser.StopTrackingRecording(); + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 200), (Action)(() => + { + RecordingMenu.IsEnabled = true; + UseDynamicModelsCheckBox.IsEnabled = true; + + })); + + } + + // Recording the relevant objects + private void RecordFrame(CLNF clnf_model, bool success, int frame_ind, RawImage frame, RawImage grayscale_frame, double time_stamp, bool output_2D_landmarks, bool output_3D_landmarks, + bool output_model_params, bool output_pose, bool output_AUs, bool output_gaze, double fx, double fy, double cx, double cy) + { + double confidence = (-clnf_model.GetConfidence()) / 2.0 + 0.5; + + List pose = new List(); + clnf_model.GetCorrectedPoseWorld(pose, fx, fy, cx, cy); + + output_features_file.Write(String.Format("{0}, {1}, {2:F3}, {3}", frame_ind, time_stamp, confidence, success ? 1 : 0)); + + if (output_gaze) + { + var gaze = face_analyser.GetGazeCamera(); + + output_features_file.Write(String.Format(", {0:F3}, {1:F3}, {2:F3}, {3:F3}, {4:F3}, {5:F3}", gaze.Item1.Item1, gaze.Item1.Item2, gaze.Item1.Item3, + gaze.Item2.Item1, gaze.Item2.Item2, gaze.Item2.Item3)); + } + + if (output_pose) + output_features_file.WriteLine(String.Format("{1:F3},{2:F3},{3:F3},{4:F3},{5:F3},{6:F3}", pose[0], pose[1], pose[2], pose[3], pose[4], pose[5])); + + if (output_2D_landmarks) + { + List> landmarks_2d = clnf_model.CalculateLandmarks(); + + for (int i = 0; i < landmarks_2d.Count; ++i) + output_features_file.Write(", {0:F2}", landmarks_2d[i].Item1); + + for (int i = 0; i < landmarks_2d.Count; ++i) + output_features_file.Write(", {0:F2}", landmarks_2d[i].Item2); + } + + if (output_3D_landmarks) + { + List landmarks_3d = clnf_model.Calculate3DLandmarks(fx, fy, cx, cy); + + for (int i = 0; i < landmarks_3d.Count; ++i) + output_features_file.Write(", {0:F2}", landmarks_3d[i].X); + + for (int i = 0; i < landmarks_3d.Count; ++i) + output_features_file.Write(", {0:F2}", landmarks_3d[i].Y); + + for (int i = 0; i < landmarks_3d.Count; ++i) + output_features_file.Write(", {0:F2}", landmarks_3d[i].Z); + } + + if (output_model_params) + { + List all_params = clnf_model.GetParams(); + + for (int i = 0; i < all_params.Count; ++i) + output_features_file.Write(String.Format(", {0,0:F5}", all_params[i])); + } + + if (output_AUs) + { + var au_regs = face_analyser.GetCurrentAUsReg(); + + foreach (var name_reg in au_reg_names) + output_features_file.Write(", {0:F2}", au_regs[name_reg]); + + var au_classes = face_analyser.GetCurrentAUsClass(); + + foreach (var name_class in au_class_names) + output_features_file.Write(", {0:F0}", au_classes[name_class]); + + } + + output_features_file.WriteLine(); + + if (record_aligned) + { + face_analyser.RecordAlignedFrame(frame_ind); + } + + if (record_HOG) + { + face_analyser.RecordHOGFrame(); + } + + if (record_tracked_vid) + { + face_analyser.RecordTrackedFace(); + } + } + + + // ---------------------------------------------------------- + // Mode handling (image, video) + // ---------------------------------------------------------- + private void SetupImageMode() + { + // Turn off recording + record_aligned = false; + record_HOG = false; + record_tracked_vid = false; + + // Turn off unneeded visualisations + show_tracked_video = true; + show_appearance = false; + show_geometry = false; + show_aus = false; + + // Actually update the GUI accordingly + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 2000), (Action)(() => + { + RecordAlignedCheckBox.IsChecked = record_aligned; + RecordTrackedVidCheckBox.IsChecked = record_tracked_vid; + RecordHOGCheckBox.IsChecked = record_HOG; + + ShowVideoCheckBox.IsChecked = true; + ShowAppearanceFeaturesCheckBox.IsChecked = false; + ShowGeometryFeaturesCheckBox.IsChecked = false; + ShowAUsCheckBox.IsChecked = false; + + VisualisationCheckBox_Click(null, null); + })); + + // TODO change what next and back buttons do? + } + + + // ---------------------------------------------------------- + // Opening Videos/Images + // ---------------------------------------------------------- + + private void videoFileOpenClick(object sender, RoutedEventArgs e) + { + new Thread(() => openVideoFile()).Start(); + } + + private void openVideoFile() + { + StopTracking(); + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 2, 0), (Action)(() => + { + var d = new OpenFileDialog(); + d.Multiselect = true; + d.Filter = "Video files|*.avi;*.wmv;*.mov;*.mpg;*.mpeg;*.mp4"; + + if (d.ShowDialog(this) == true) + { + + string[] video_files = d.FileNames; + + processing_thread = new Thread(() => ProcessingLoop(video_files)); + processing_thread.Start(); + + } + })); + } + + + private void imageFileOpenClick(object sender, RoutedEventArgs e) + { + new Thread(() => imageOpen()).Start(); + } + + private void imageOpen() + { + StopTracking(); + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 2, 0), (Action)(() => + { + var d = new OpenFileDialog(); + d.Multiselect = true; + d.Filter = "Image files|*.jpg;*.jpeg;*.bmp;*.png;*.gif"; + + if (d.ShowDialog(this) == true) + { + + string[] image_files = d.FileNames; + + processing_thread = new Thread(() => ProcessingLoop(image_files, -3)); + processing_thread.Start(); + + } + })); + } + + private void imageSequenceFileOpenClick(object sender, RoutedEventArgs e) + { + new Thread(() => imageSequenceOpen()).Start(); + } + + private void imageSequenceOpen() + { + StopTracking(); + + Dispatcher.Invoke(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 2, 0), (Action)(() => + { + var d = new OpenFileDialog(); + d.Multiselect = true; + d.Filter = "Image files|*.jpg;*.jpeg;*.bmp;*.png;*.gif"; + + if (d.ShowDialog(this) == true) + { + + string[] image_files = d.FileNames; + + processing_thread = new Thread(() => ProcessingLoop(image_files, -2)); + processing_thread.Start(); + + } + })); + } + + // -------------------------------------------------------- + // Button handling + // -------------------------------------------------------- + + // Cleanup stuff when closing the window + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + if (processing_thread != null) + { + // Stop capture and tracking + thread_running = false; + processing_thread.Join(); + + capture.Dispose(); + } + face_analyser.Dispose(); + } + + // Stopping the tracking + private void StopButton_Click(object sender, RoutedEventArgs e) + { + if (processing_thread != null) + { + // Stop capture and tracking + thread_paused = false; + thread_running = false; + processing_thread.Join(); + + PauseButton.IsEnabled = false; + NextFrameButton.IsEnabled = false; + NextFiveFramesButton.IsEnabled = false; + StopButton.IsEnabled = false; + ResetButton.IsEnabled = false; + RecordingMenu.IsEnabled = true; + + UseDynamicModelsCheckBox.IsEnabled = true; + } + } + + // Resetting the tracker + private void ResetButton_Click(object sender, RoutedEventArgs e) + { + if (processing_thread != null) + { + // Stop capture and tracking + reset = true; + } + } + + private void PauseButton_Click(object sender, RoutedEventArgs e) + { + if (processing_thread != null) + { + // Stop capture and tracking + thread_paused = !thread_paused; + + ResetButton.IsEnabled = !thread_paused; + + NextFrameButton.IsEnabled = thread_paused; + NextFiveFramesButton.IsEnabled = thread_paused; + + if (thread_paused) + { + PauseButton.Content = "Resume"; + } + else + { + PauseButton.Content = "Pause"; + } + } + } + + private void SkipButton_Click(object sender, RoutedEventArgs e) + { + if (sender.Equals(NextFrameButton)) + { + skip_frames += 1; + } + else if (sender.Equals(NextFiveFramesButton)) + { + skip_frames += 5; + } + } + + + private void VisualisationCheckBox_Click(object sender, RoutedEventArgs e) + { + show_tracked_video = ShowVideoCheckBox.IsChecked; + show_appearance = ShowAppearanceFeaturesCheckBox.IsChecked; + show_geometry = ShowGeometryFeaturesCheckBox.IsChecked; + show_aus = ShowAUsCheckBox.IsChecked; + + // Collapsing or restoring the windows here + if (!show_tracked_video) + { + VideoBorder.Visibility = System.Windows.Visibility.Collapsed; + MainGrid.ColumnDefinitions[0].Width = new GridLength(0, GridUnitType.Star); + } + else + { + VideoBorder.Visibility = System.Windows.Visibility.Visible; + MainGrid.ColumnDefinitions[0].Width = new GridLength(2.1, GridUnitType.Star); + } + + if (!show_appearance) + { + AppearanceBorder.Visibility = System.Windows.Visibility.Collapsed; + MainGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Star); + } + else + { + AppearanceBorder.Visibility = System.Windows.Visibility.Visible; + MainGrid.ColumnDefinitions[1].Width = new GridLength(0.8, GridUnitType.Star); + } + + // Collapsing or restoring the windows here + if (!show_geometry) + { + GeometryBorder.Visibility = System.Windows.Visibility.Collapsed; + MainGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Star); + } + else + { + GeometryBorder.Visibility = System.Windows.Visibility.Visible; + MainGrid.ColumnDefinitions[2].Width = new GridLength(1.0, GridUnitType.Star); + } + + // Collapsing or restoring the windows here + if (!show_aus) + { + ActionUnitBorder.Visibility = System.Windows.Visibility.Collapsed; + MainGrid.ColumnDefinitions[3].Width = new GridLength(0, GridUnitType.Star); + } + else + { + ActionUnitBorder.Visibility = System.Windows.Visibility.Visible; + MainGrid.ColumnDefinitions[3].Width = new GridLength(1.6, GridUnitType.Star); + } + + } + + + private void recordCheckBox_click(object sender, RoutedEventArgs e) + { + record_aligned = RecordAlignedCheckBox.IsChecked; + record_HOG = RecordHOGCheckBox.IsChecked; + record_tracked_vid = RecordTrackedVidCheckBox.IsChecked; + } + + private void UseDynamicModelsCheckBox_Click(object sender, RoutedEventArgs e) + { + dynamic_AU_shift = UseDynamicShiftingCheckBox.IsChecked; + dynamic_AU_scale = UseDynamicScalingCheckBox.IsChecked; + + if (use_dynamic_models != UseDynamicModelsCheckBox.IsChecked) + { + // Change the face analyser, this should be safe as the model is only allowed to change when not running + String root = AppDomain.CurrentDomain.BaseDirectory; + face_analyser = new FaceAnalyserManaged(root, UseDynamicModelsCheckBox.IsChecked); + } + use_dynamic_models = UseDynamicModelsCheckBox.IsChecked; + } + + } +} diff --git a/gui/OpenFaceOffline/OpenFaceOffline.csproj b/gui/OpenFaceOffline/OpenFaceOffline.csproj new file mode 100644 index 00000000..c9e4d7c3 --- /dev/null +++ b/gui/OpenFaceOffline/OpenFaceOffline.csproj @@ -0,0 +1,184 @@ + + + + + Debug + AnyCPU + {A4760F41-2B1F-4144-B7B2-62785AFFE79B} + WinExe + Properties + OpenFaceOffline + OpenFaceOffline + v4.5.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + + AnyCPU + true + full + false + ..\..\x64\Debug\ + DEBUG;TRACE + prompt + 4 + + + x64 + pdbonly + true + ..\..\x64\Release\ + TRACE + prompt + 4 + + + true + ..\..\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + ..\..\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + logo1.ico + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + BarGraph.xaml + + + BarGraphHorizontal.xaml + + + MultiBarGraph.xaml + + + MultiBarGraphHorz.xaml + + + OverlayImage.xaml + + + SimpleImage.xaml + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + {78196985-ee54-411f-822b-5a23edf80642} + CppInerop + + + + + + + + xcopy /I /E /Y /D "$(ProjectDir)logo1.ico" "$(SolutionDir)/$(Configuration)" + + + \ No newline at end of file diff --git a/gui/OpenFaceOffline/Properties/AssemblyInfo.cs b/gui/OpenFaceOffline/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..74fe00e8 --- /dev/null +++ b/gui/OpenFaceOffline/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenFaceOffline")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("OpenFaceOffline")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/gui/OpenFaceOffline/Properties/Resources.Designer.cs b/gui/OpenFaceOffline/Properties/Resources.Designer.cs new file mode 100644 index 00000000..644558ec --- /dev/null +++ b/gui/OpenFaceOffline/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace OpenFaceOffline.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenFaceOffline.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/gui/OpenFaceOffline/Properties/Resources.resx b/gui/OpenFaceOffline/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/gui/OpenFaceOffline/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/gui/OpenFaceOffline/Properties/Settings.Designer.cs b/gui/OpenFaceOffline/Properties/Settings.Designer.cs new file mode 100644 index 00000000..6ab083e5 --- /dev/null +++ b/gui/OpenFaceOffline/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace OpenFaceOffline.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/gui/OpenFaceOffline/Properties/Settings.settings b/gui/OpenFaceOffline/Properties/Settings.settings new file mode 100644 index 00000000..033d7a5e --- /dev/null +++ b/gui/OpenFaceOffline/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/gui/OpenFaceOffline/UI_items/BarGraph.xaml b/gui/OpenFaceOffline/UI_items/BarGraph.xaml new file mode 100644 index 00000000..2101d740 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/BarGraph.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/gui/OpenFaceOffline/UI_items/BarGraph.xaml.cs b/gui/OpenFaceOffline/UI_items/BarGraph.xaml.cs new file mode 100644 index 00000000..35303f96 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/BarGraph.xaml.cs @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +using System.Windows.Controls; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for BarGraph.xaml + /// + public partial class BarGraph : UserControl + { + private double targetValue = 0; + + public BarGraph() + { + InitializeComponent(); + } + + public void SetValue(double value) + { + targetValue = 1.5 * value; + if (targetValue > 0) + { + if (targetValue > barContainerPos.ActualHeight) + targetValue = barContainerPos.ActualHeight; + + barPos.Height = targetValue; + barNeg.Height = 0; + } + if (targetValue < 0) + { + if (-targetValue > barContainerNeg.ActualHeight) + targetValue = -barContainerNeg.ActualHeight; + + barPos.Height = 0; + barNeg.Height = -targetValue; + } + } + + } +} diff --git a/gui/OpenFaceOffline/UI_items/BarGraphHorizontal.xaml b/gui/OpenFaceOffline/UI_items/BarGraphHorizontal.xaml new file mode 100644 index 00000000..f5ccbe5e --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/BarGraphHorizontal.xaml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/gui/OpenFaceOffline/UI_items/BarGraphHorizontal.xaml.cs b/gui/OpenFaceOffline/UI_items/BarGraphHorizontal.xaml.cs new file mode 100644 index 00000000..44a7d1a2 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/BarGraphHorizontal.xaml.cs @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for BarGraphHorizontal.xaml + /// + public partial class BarGraphHorizontal : UserControl + { + double targetValue = 0; + + public BarGraphHorizontal(String label) + { + InitializeComponent(); + Label.Content = label; + } + + public void SetValue(double value) + { + targetValue = value; + barPos.Width = targetValue * barContainerPos.ActualWidth; + } + + public double GetTarget() + { + return targetValue; + } + } +} diff --git a/gui/OpenFaceOffline/UI_items/MultiBarGraph.xaml b/gui/OpenFaceOffline/UI_items/MultiBarGraph.xaml new file mode 100644 index 00000000..836c9742 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/MultiBarGraph.xaml @@ -0,0 +1,11 @@ + + + + + diff --git a/gui/OpenFaceOffline/UI_items/MultiBarGraph.xaml.cs b/gui/OpenFaceOffline/UI_items/MultiBarGraph.xaml.cs new file mode 100644 index 00000000..7f329096 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/MultiBarGraph.xaml.cs @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for MultiBarGraph.xaml + /// + public partial class MultiBarGraph : UserControl + { + + int num_bars = 0; + List graphs; + + public MultiBarGraph() + { + InitializeComponent(); + + graphs = new List(); + } + + public void Update(List data) + { + // Create new bars if necessary + if (num_bars != data.Count) + { + num_bars = data.Count; + barGrid.Children.Clear(); + foreach (var value in data) + { + BarGraph newBar = new BarGraph(); + graphs.Add(newBar); + barGrid.ColumnDefinitions.Add(new ColumnDefinition()); + Grid.SetColumn(newBar, graphs.Count); + barGrid.Children.Add(newBar); + + } + } + + // Update the bars + for (int i = 0; i < data.Count; ++i) + { + graphs[i].SetValue(data[i]); + } + } + } +} diff --git a/gui/OpenFaceOffline/UI_items/MultiBarGraphHorz.xaml b/gui/OpenFaceOffline/UI_items/MultiBarGraphHorz.xaml new file mode 100644 index 00000000..db5728f6 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/MultiBarGraphHorz.xaml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/gui/OpenFaceOffline/UI_items/MultiBarGraphHorz.xaml.cs b/gui/OpenFaceOffline/UI_items/MultiBarGraphHorz.xaml.cs new file mode 100644 index 00000000..f9af7c78 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/MultiBarGraphHorz.xaml.cs @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for MultiBarGraphHorz.xaml + /// + public partial class MultiBarGraphHorz : UserControl + { + int num_bars = 0; + Dictionary graphs; + + // Name mapping + Dictionary mapping; + + public MultiBarGraphHorz() + { + InitializeComponent(); + + graphs = new Dictionary(); + + mapping = new Dictionary(); + mapping["AU01"] = "Inner Brow raiser"; + mapping["AU02"] = "Outer Brow raiser"; + mapping["AU04"] = "Brow lowerer"; + mapping["AU05"] = "Upper lid raiser"; + mapping["AU06"] = "Cheek raiser"; + mapping["AU07"] = "Lid tightener"; + mapping["AU09"] = "Nose wrinkler"; + mapping["AU10"] = "Upper lip raiser"; + mapping["AU12"] = "Lip corner puller (smile)"; + mapping["AU14"] = "Dimpler"; + mapping["AU15"] = "Lip corner depressor"; + mapping["AU17"] = "Chin Raiser"; + mapping["AU20"] = "Lip Stretcher"; + mapping["AU23"] = "Lip tightener"; + mapping["AU25"] = "Lips part"; + mapping["AU26"] = "Jaw drop"; + mapping["AU28"] = "Lip suck"; + mapping["AU45"] = "Blink"; + + + + } + + public void Update(Dictionary data) + { + // Create new bars if necessary + if (num_bars != data.Count) + { + num_bars = data.Count; + barGrid.Children.Clear(); + + // Make sure AUs are sorted + var data_labels = data.Keys.ToList(); + data_labels.Sort(); + + foreach (var label in data_labels) + { + BarGraphHorizontal newBar = new BarGraphHorizontal(label + " - " + mapping[label]); + barGrid.RowDefinitions.Add(new RowDefinition()); + Grid.SetRow(newBar, graphs.Count); + graphs.Add(label, newBar); + barGrid.Children.Add(newBar); + } + } + + // Update the bars + foreach (var value in data) + { + double old_value = graphs[value.Key].GetTarget(); + // some smoothing as well + graphs[value.Key].SetValue(old_value * 0.15 + 0.85 * value.Value); + } + } + } +} diff --git a/gui/OpenFaceOffline/UI_items/OverlayImage.xaml b/gui/OpenFaceOffline/UI_items/OverlayImage.xaml new file mode 100644 index 00000000..8dcdaea5 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/OverlayImage.xaml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/gui/OpenFaceOffline/UI_items/OverlayImage.xaml.cs b/gui/OpenFaceOffline/UI_items/OverlayImage.xaml.cs new file mode 100644 index 00000000..b7bce904 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/OverlayImage.xaml.cs @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for OverlayImage.xaml + /// + public partial class OverlayImage : Image + { + public OverlayImage() + { + InitializeComponent(); + OverlayLines = new List>(); + OverlayPoints = new List(); + GazeLines = new List>(); + + Progress = -1; + } + + protected override void OnRender(DrawingContext dc) + { + base.OnRender(dc); + + if (OverlayLines == null) + OverlayLines = new List>(); + + if (OverlayPoints == null) + OverlayPoints = new List(); + + if (Source == null || !(Source is WriteableBitmap)) + return; + + var width = ((WriteableBitmap)Source).PixelWidth; + var height = ((WriteableBitmap)Source).PixelHeight; + + foreach (var line in OverlayLines) + { + + var p1 = new Point(ActualWidth * line.Item1.X / width, ActualHeight * line.Item1.Y / height); + var p2 = new Point(ActualWidth * line.Item2.X / width, ActualHeight * line.Item2.Y / height); + + dc.DrawLine(new Pen(new SolidColorBrush(Color.FromArgb(200, (byte)(100 + (155 * (1 - Confidence))), (byte)(100 + (155 * Confidence)), 100)), 2), p1, p2); + } + + foreach (var line in GazeLines) + { + + var p1 = new Point(ActualWidth * line.Item1.X / width, ActualHeight * line.Item1.Y / height); + var p2 = new Point(ActualWidth * line.Item2.X / width, ActualHeight * line.Item2.Y / height); + + dc.DrawLine(new Pen(new SolidColorBrush(Color.FromArgb(200, (byte)(240), (byte)(30), (byte)100)), 3), p1, p2); + + } + + foreach (var p in OverlayPoints) + { + + var q = new Point(ActualWidth * p.X / width, ActualHeight * p.Y / height); + + dc.DrawEllipse(new SolidColorBrush(Color.FromArgb((byte)(200 * Confidence), 255, 255, 100)), null, q, 2, 2); + } + + double scaling = ActualWidth / 400.0; + + int confidence_width = (int)(107.0 * scaling); + int confidence_height = (int)(18.0 * scaling); + + Brush conf_brush = new SolidColorBrush(Color.FromRgb((byte)((1 - Confidence) * 255), (byte)(Confidence * 255), (byte)40)); + dc.DrawRoundedRectangle(conf_brush, new Pen(Brushes.Black, 0.5 * scaling), new Rect(ActualWidth - confidence_width - 1, 0, confidence_width, confidence_height), 3.0 * scaling, 3.0 * scaling); + + FormattedText txt = new FormattedText("Confidence: " + (int)(100 * Confidence) + "%", System.Globalization.CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, new Typeface("Verdana"), 12.0 * scaling, Brushes.Black); + dc.DrawText(txt, new Point(ActualWidth - confidence_width + 2, 2)); + + int fps_width = (int)(52.0 * scaling); + int fps_height = (int)(18.0 * scaling); + + dc.DrawRoundedRectangle(Brushes.WhiteSmoke, new Pen(Brushes.Black, 0.5 * scaling), new Rect(0, 0, fps_width, fps_height), 3.0 * scaling, 3.0 * scaling); + FormattedText fps_txt = new FormattedText("FPS: " + (int)FPS, System.Globalization.CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, new Typeface("Verdana"), 12.0 * scaling, Brushes.Black); + dc.DrawText(fps_txt, new Point(2.0 * scaling, 0)); + + old_width = width; + old_height = height; + + // Drawing a progress bar at the bottom of the image + if (Progress > 0) + { + int progress_bar_height = (int)(6.0 * scaling); + dc.DrawRectangle(Brushes.GreenYellow, new Pen(Brushes.Black, 0.5 * scaling), new Rect(0, ActualHeight - progress_bar_height, Progress * ActualWidth, progress_bar_height)); + } + + } + + public List> OverlayLines { get; set; } + public List> GazeLines { get; set; } + public List OverlayPoints { get; set; } + public double Confidence { get; set; } + public double FPS { get; set; } + + // 0 to 1 indicates how much video has been processed so far + public double Progress { get; set; } + + int old_width; + int old_height; + } +} diff --git a/gui/OpenFaceOffline/UI_items/SimpleImage.xaml b/gui/OpenFaceOffline/UI_items/SimpleImage.xaml new file mode 100644 index 00000000..9cf612d7 --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/SimpleImage.xaml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/gui/OpenFaceOffline/UI_items/SimpleImage.xaml.cs b/gui/OpenFaceOffline/UI_items/SimpleImage.xaml.cs new file mode 100644 index 00000000..19cb0ebb --- /dev/null +++ b/gui/OpenFaceOffline/UI_items/SimpleImage.xaml.cs @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace OpenFaceOffline +{ + /// + /// Interaction logic for OverlayImage.xaml + /// + public partial class SimpleImage : Image + { + public SimpleImage() + { + InitializeComponent(); + } + + protected override void OnRender(DrawingContext dc) + { + base.OnRender(dc); + + if (Source == null || !(Source is WriteableBitmap)) + return; + } + } +} diff --git a/gui/OpenFaceOffline/logo1.ico b/gui/OpenFaceOffline/logo1.ico new file mode 100644 index 00000000..60a49af1 Binary files /dev/null and b/gui/OpenFaceOffline/logo1.ico differ diff --git a/lib/local/CamCom/CamCom.props b/lib/local/CamCom/CamCom.props new file mode 100644 index 00000000..9e4a2663 --- /dev/null +++ b/lib/local/CamCom/CamCom.props @@ -0,0 +1,15 @@ + + + + + + + + $(SolutionDir)lib\local\CamCom;%(AdditionalIncludeDirectories) + + + dxva2.lib;evr.lib;mf.lib;mfplat.lib;mfplay.lib;mfreadwrite.lib;mfuuid.lib;Strmiids.lib;%(AdditionalDependencies) + + + + \ No newline at end of file diff --git a/lib/local/CamCom/CamCom.vcxproj b/lib/local/CamCom/CamCom.vcxproj new file mode 100644 index 00000000..5866b40d --- /dev/null +++ b/lib/local/CamCom/CamCom.vcxproj @@ -0,0 +1,167 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {0CEC6A75-17BA-4DC5-9405-C74154921E60} + Win32Proj + CamCom + 8.1 + + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ./;%(AdditionalIncludeDirectories) + + + Console + true + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ./;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ./;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ./;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/local/CamCom/CamCom.vcxproj.filters b/lib/local/CamCom/CamCom.vcxproj.filters new file mode 100644 index 00000000..06e1b61a --- /dev/null +++ b/lib/local/CamCom/CamCom.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/lib/local/CamCom/camera_helper.cpp b/lib/local/CamCom/camera_helper.cpp new file mode 100644 index 00000000..0b060ad7 --- /dev/null +++ b/lib/local/CamCom/camera_helper.cpp @@ -0,0 +1,192 @@ +#include "stdafx.h" + +#include "camera_helper.h" + +//#include + +namespace { + namespace internal { + template struct property_traits {}; + + template + struct camera_control_property_traits + { + typedef IAMCameraControl Interface; + static long id() { return Id; } + }; + + template + struct video_proc_amp_property_traits + { + typedef IAMVideoProcAmp Interface; + static long id() { return Id; } + }; + + template<> struct property_traits < camera::Exposure > : public camera_control_property_traits {}; + template<> struct property_traits < camera::Focus > : public camera_control_property_traits{}; + template<> struct property_traits < camera::Zoom > : public camera_control_property_traits{}; + template<> struct property_traits < camera::Pan > : public camera_control_property_traits{}; + template<> struct property_traits < camera::Tilt > : public camera_control_property_traits{}; + template<> struct property_traits < camera::Roll > : public camera_control_property_traits{}; + template<> struct property_traits < camera::Iris > : public camera_control_property_traits{}; + + template<> struct property_traits < camera::Brightness > : public video_proc_amp_property_traits{}; + template<> struct property_traits < camera::Contrast > : public video_proc_amp_property_traits{}; + template<> struct property_traits < camera::Hue > : public video_proc_amp_property_traits{}; + template<> struct property_traits < camera::Saturation > : public video_proc_amp_property_traits{}; + template<> struct property_traits < camera::Sharpness > : public video_proc_amp_property_traits{}; + template<> struct property_traits < camera::Gamma > : public video_proc_amp_property_traits{}; + template<> struct property_traits < camera::ColorEnable > : public video_proc_amp_property_traits{}; + template<> struct property_traits < camera::WhiteBalance > : public video_proc_amp_property_traits{}; + template<> struct property_traits < camera::BacklightCompensation > : public video_proc_amp_property_traits{}; + template<> struct property_traits < camera::Gain > : public video_proc_amp_property_traits < VideoProcAmp_Gain > {}; + + template struct interface_traits {}; + template<> struct interface_traits < IAMCameraControl > + { + static long auto_flag() { return CameraControl_Flags_Auto; } + static long manual_flag() { return CameraControl_Flags_Manual; } + }; + template<> struct interface_traits + { + static long auto_flag() { return VideoProcAmp_Flags_Auto; } + static long manual_flag() { return VideoProcAmp_Flags_Manual; } + }; + + template + bool has(long id, const SourcePtr& source_ptr) { + comet::com_ptr pInterface = comet::com_cast(source_ptr); + if (!pInterface) return false; + long value, flags; + HRESULT hr = pInterface->Get(id, &value, &flags); + return SUCCEEDED(hr); + } + + template + cam_prop_range get_range(long id, const SourcePtr& source_ptr) { + comet::com_ptr pInterface = comet::try_cast(source_ptr); + cam_prop_range range; + long flags; + pInterface->GetRange(id, &range.min, &range.max, &range.step, &range.defaultValue, &flags) | comet::raise_exception; + range.canBeAuto = (flags & interface_traits::auto_flag()) != 0; + range.canBeManual = (flags & interface_traits::manual_flag()) != 0; + return range; + } + + template + cam_prop_value get(long id, const SourcePtr& source_ptr) { + comet::com_ptr pInterface = comet::try_cast(source_ptr); + cam_prop_value value; + long flags; + pInterface->Get(id, &value.value, &flags) | comet::raise_exception; + value.isAuto = (flags & interface_traits::auto_flag()) != 0 || (flags & interface_traits::manual_flag()) == 0; + return value; + } + + template + void set(long id, const cam_prop_value& value, const SourcePtr& source_ptr) { + comet::com_ptr pInterface = comet::try_cast(source_ptr); + long flags = value.isAuto ? interface_traits::auto_flag() : interface_traits::manual_flag(); + pInterface->Set(id, value.value, flags) | comet::raise_exception; + } + } +} + +#define MAP_OVER_CAMERA_PROPERTIES(FUNC) \ + FUNC(::camera::Exposure, Exposure) \ + FUNC(::camera::Focus, Focus) \ + FUNC(::camera::Zoom, Zoom) \ + FUNC(::camera::Pan, Pan) \ + FUNC(::camera::Tilt, Tilt) \ + FUNC(::camera::Roll, Roll) \ + FUNC(::camera::Iris, Iris) \ + FUNC(::camera::Brightness, Brightness) \ + FUNC(::camera::Contrast, Contrast) \ + FUNC(::camera::Hue, Hue) \ + FUNC(::camera::Saturation, Saturation) \ + FUNC(::camera::Sharpness, Sharpness) \ + FUNC(::camera::Gamma, Gamma) \ + FUNC(::camera::ColorEnable, Color Enable) \ + FUNC(::camera::WhiteBalance, White Balance) \ + FUNC(::camera::BacklightCompensation, Backlight Compensation) \ + FUNC(::camera::Gain, Gain) + + +bool camera::has(Property property) const +{ + switch (property) { +#define CASE(PROP, NAME) case PROP: return ::internal::has<::internal::property_traits::Interface>(::internal::property_traits::id(), source_ptr); + MAP_OVER_CAMERA_PROPERTIES(CASE) +#undef CASE + default: + return false; + } +} +cam_prop_range camera::get_range(Property property) const +{ + switch (property) { +#define CASE(PROP, NAME) case PROP: return ::internal::get_range<::internal::property_traits::Interface>(::internal::property_traits::id(), source_ptr); + MAP_OVER_CAMERA_PROPERTIES(CASE) +#undef CASE + default: + throw std::runtime_error("No such property: " + std::string(camera::property_name(property))); + } +} +cam_prop_value camera::get(Property property) const +{ + switch (property) { +#define CASE(PROP, NAME) case PROP: return ::internal::get<::internal::property_traits::Interface>(::internal::property_traits::id(), source_ptr); + MAP_OVER_CAMERA_PROPERTIES(CASE) +#undef CASE + default: + throw std::runtime_error("No such property: " + std::string(camera::property_name(property))); + } +} +void camera::set(Property property, const cam_prop_value& value) +{ + switch (property) { +#define CASE(PROP, NAME) case PROP: ::internal::set<::internal::property_traits::Interface>(::internal::property_traits::id(), value, source_ptr); break; + MAP_OVER_CAMERA_PROPERTIES(CASE) +#undef CASE + } +} + +std::vector camera::list_properties() +{ + std::vector properties; + properties.push_back(Exposure); // CameraControl + properties.push_back(Focus); + properties.push_back(Zoom); + properties.push_back(Pan); + properties.push_back(Tilt); + properties.push_back(Roll); + properties.push_back(Iris); + + properties.push_back(Brightness); + properties.push_back(Contrast); + properties.push_back(Hue); + properties.push_back(Saturation); + properties.push_back(Sharpness); + properties.push_back(Gamma); + properties.push_back(ColorEnable); + properties.push_back(WhiteBalance); + properties.push_back(BacklightCompensation); + properties.push_back(Gain); + + return properties; +//#define PROPENTRY(PROP, NAME) PROP, +// MAP_OVER_CAMERA_PROPERTIES(PROPENTRY) +//#undef PROPENTRY +// }; +} + +const char* camera::property_name(Property property) +{ + switch (property) { +#define CASE(PROP, NAME) case PROP: return #NAME; + MAP_OVER_CAMERA_PROPERTIES(CASE) +#undef CASE + default: + return "UNKNOWN"; + } +} diff --git a/lib/local/CamCom/camera_helper.h b/lib/local/CamCom/camera_helper.h new file mode 100644 index 00000000..68746475 --- /dev/null +++ b/lib/local/CamCom/camera_helper.h @@ -0,0 +1,720 @@ +#ifndef camera_helper_h__ +#define camera_helper_h__ + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +namespace comet { + +#define MAKE_COMET_COMTYPE(T, BASE) \ + template<> struct comtype< ::T > \ + { \ + static const IID& uuid() { return IID_ ## T; } \ + typedef ::BASE base; \ + } + + MAKE_COMET_COMTYPE(IMF2DBuffer, IUnknown); + MAKE_COMET_COMTYPE(IAMVideoProcAmp, IUnknown); + MAKE_COMET_COMTYPE(IAMCameraControl, IUnknown); +} + +struct cam_prop_range +{ + long min; + long max; + long step; + long defaultValue; + bool canBeAuto; + bool canBeManual; +}; + +inline std::ostream& operator<<(std::ostream& os, const cam_prop_range& val) +{ + os << "[" << val.min << ":" << val.step << ":" << val.max << "], " << val.defaultValue; + if (val.canBeAuto && val.canBeManual) + os << " (auto/manual)"; + else if (val.canBeAuto) + os << " (auto)"; + else if (val.canBeManual) + os << " (manual)"; + return os; +} + +struct cam_prop_value +{ + long value; + bool isAuto; + + cam_prop_value() : value(0), isAuto(true) + { + } + + cam_prop_value(long value, bool isAuto = false) : value(value), isAuto(isAuto) + { + } + + operator long() const + { + return value; + } + + static cam_prop_value AUTO(long value = 0) { + return cam_prop_value(value, true); + }; + static cam_prop_value MANUAL(long value = 0) { + return cam_prop_value(value, false); + }; +}; + +inline std::ostream& operator<<(std::ostream& os, const cam_prop_value& val) +{ + os << val.value; + os << " ("; + if (val.isAuto) + os << "auto"; + else + os << "manual"; + os << ")"; + return os; +} + +namespace MediaFormat +{ + enum e + { + Unknown, + RGB32, // MFVideoFormat_RGB32 + ARGB32, // MFVideoFormat_ARGB32 + RGB24, // MFVideoFormat_RGB24 + RGB555, // MFVideoFormat_RGB555 + RGB565, // MFVideoFormat_RGB565 + RGB8, // MFVideoFormat_RGB8 + AI44, // MFVideoFormat_AI44 + AYUV, // MFVideoFormat_AYUV + YUY2, // MFVideoFormat_YUY2 + YVYU, // MFVideoFormat_YVYU + YVU9, // MFVideoFormat_YVU9 + UYVY, // MFVideoFormat_UYVY + NV11, // MFVideoFormat_NV11 + NV12, // MFVideoFormat_NV12 + YV12, // MFVideoFormat_YV12 + I420, // MFVideoFormat_I420 + IYUV, // MFVideoFormat_IYUV + Y210, // MFVideoFormat_Y210 + Y216, // MFVideoFormat_Y216 + Y410, // MFVideoFormat_Y410 + Y416, // MFVideoFormat_Y416 + Y41P, // MFVideoFormat_Y41P + Y41T, // MFVideoFormat_Y41T + Y42T, // MFVideoFormat_Y42T + P210, // MFVideoFormat_P210 + P216, // MFVideoFormat_P216 + P010, // MFVideoFormat_P010 + P016, // MFVideoFormat_P016 + v210, // MFVideoFormat_v210 + v216, // MFVideoFormat_v216 + v410, // MFVideoFormat_v410 + MP43, // MFVideoFormat_MP43 + MP4S, // MFVideoFormat_MP4S + M4S2, // MFVideoFormat_M4S2 + MP4V, // MFVideoFormat_MP4V + WMV1, // MFVideoFormat_WMV1 + WMV2, // MFVideoFormat_WMV2 + WMV3, // MFVideoFormat_WMV3 + WVC1, // MFVideoFormat_WVC1 + MSS1, // MFVideoFormat_MSS1 + MSS2, // MFVideoFormat_MSS2 + MPG1, // MFVideoFormat_MPG1 + DVSL, // MFVideoFormat_DVSL + DVSD, // MFVideoFormat_DVSD + DVHD, // MFVideoFormat_DVHD + DV25, // MFVideoFormat_DV25 + DV50, // MFVideoFormat_DV50 + DVH1, // MFVideoFormat_DVH1 + DVC, // MFVideoFormat_DVC + H264, // MFVideoFormat_H264 + MJPG, // MFVideoFormat_MJPG + YUV_420O, // MFVideoFormat_420O + H263, // MFVideoFormat_H263 + H264_ES, // MFVideoFormat_H264_ES + MPEG2, // MFVideoFormat_MPEG2 + }; + + inline e fromGUID(GUID guid) + { + if (guid == MFVideoFormat_RGB32) return RGB32; + else if (guid == MFVideoFormat_ARGB32) return ARGB32; + else if (guid == MFVideoFormat_RGB24) return RGB24; + else if (guid == MFVideoFormat_RGB555) return RGB555; + else if (guid == MFVideoFormat_RGB565) return RGB565; + else if (guid == MFVideoFormat_RGB8) return RGB8; + else if (guid == MFVideoFormat_AI44) return AI44; + else if (guid == MFVideoFormat_AYUV) return AYUV; + else if (guid == MFVideoFormat_YUY2) return YUY2; + else if (guid == MFVideoFormat_YVYU) return YVYU; + else if (guid == MFVideoFormat_YVU9) return YVU9; + else if (guid == MFVideoFormat_UYVY) return UYVY; + else if (guid == MFVideoFormat_NV11) return NV11; + else if (guid == MFVideoFormat_NV12) return NV12; + else if (guid == MFVideoFormat_YV12) return YV12; + else if (guid == MFVideoFormat_I420) return I420; + else if (guid == MFVideoFormat_IYUV) return IYUV; + else if (guid == MFVideoFormat_Y210) return Y210; + else if (guid == MFVideoFormat_Y216) return Y216; + else if (guid == MFVideoFormat_Y410) return Y410; + else if (guid == MFVideoFormat_Y416) return Y416; + else if (guid == MFVideoFormat_Y41P) return Y41P; + else if (guid == MFVideoFormat_Y41T) return Y41T; + else if (guid == MFVideoFormat_Y42T) return Y42T; + else if (guid == MFVideoFormat_P210) return P210; + else if (guid == MFVideoFormat_P216) return P216; + else if (guid == MFVideoFormat_P010) return P010; + else if (guid == MFVideoFormat_P016) return P016; + else if (guid == MFVideoFormat_v210) return v210; + else if (guid == MFVideoFormat_v216) return v216; + else if (guid == MFVideoFormat_v410) return v410; + else if (guid == MFVideoFormat_MP43) return MP43; + else if (guid == MFVideoFormat_MP4S) return MP4S; + else if (guid == MFVideoFormat_M4S2) return M4S2; + else if (guid == MFVideoFormat_MP4V) return MP4V; + else if (guid == MFVideoFormat_WMV1) return WMV1; + else if (guid == MFVideoFormat_WMV2) return WMV2; + else if (guid == MFVideoFormat_WMV3) return WMV3; + else if (guid == MFVideoFormat_WVC1) return WVC1; + else if (guid == MFVideoFormat_MSS1) return MSS1; + else if (guid == MFVideoFormat_MSS2) return MSS2; + else if (guid == MFVideoFormat_MPG1) return MPG1; + else if (guid == MFVideoFormat_DVSL) return DVSL; + else if (guid == MFVideoFormat_DVSD) return DVSD; + else if (guid == MFVideoFormat_DVHD) return DVHD; + else if (guid == MFVideoFormat_DV25) return DV25; + else if (guid == MFVideoFormat_DV50) return DV50; + else if (guid == MFVideoFormat_DVH1) return DVH1; + else if (guid == MFVideoFormat_DVC) return DVC; + else if (guid == MFVideoFormat_H264) return H264; + else if (guid == MFVideoFormat_MJPG) return MJPG; + else if (guid == MFVideoFormat_420O) return YUV_420O; + else if (guid == MFVideoFormat_H263) return H263; + else if (guid == MFVideoFormat_H264_ES) return H264_ES; + else if (guid == MFVideoFormat_MPEG2) return MPEG2; + else return Unknown; + } + + inline const char* to_string (const MediaFormat::e& format) + { + switch (format) + { + case RGB32: return "RGB32"; + case ARGB32: return "ARGB32"; + case RGB24: return "RGB24"; + case RGB555: return "RGB555"; + case RGB565: return "RGB565"; + case RGB8: return "RGB8"; + case AI44: return "AI44"; + case AYUV: return "AYUV"; + case YUY2: return "YUY2"; + case YVYU: return "YVYU"; + case YVU9: return "YVU9"; + case UYVY: return "UYVY"; + case NV11: return "NV11"; + case NV12: return "NV12"; + case YV12: return "YV12"; + case I420: return "I420"; + case IYUV: return "IYUV"; + case Y210: return "Y210"; + case Y216: return "Y216"; + case Y410: return "Y410"; + case Y416: return "Y416"; + case Y41P: return "Y41P"; + case Y41T: return "Y41T"; + case Y42T: return "Y42T"; + case P210: return "P210"; + case P216: return "P216"; + case P010: return "P010"; + case P016: return "P016"; + case v210: return "v210"; + case v216: return "v216"; + case v410: return "v410"; + case MP43: return "MP43"; + case MP4S: return "MP4S"; + case M4S2: return "M4S2"; + case MP4V: return "MP4V"; + case WMV1: return "WMV1"; + case WMV2: return "WMV2"; + case WMV3: return "WMV3"; + case WVC1: return "WVC1"; + case MSS1: return "MSS1"; + case MSS2: return "MSS2"; + case MPG1: return "MPG1"; + case DVSL: return "DVSL"; + case DVSD: return "DVSD"; + case DVHD: return "DVHD"; + case DV25: return "DV25"; + case DV50: return "DV50"; + case DVH1: return "DVH1"; + case DVC: return "DVC"; + case H264: return "H264"; + case MJPG: return "MJPG"; + case YUV_420O: return "420O"; + case H263: return "H263"; + case H264_ES: return "H264_ES"; + case MPEG2: return "MPEG2"; + default: return "Unknown"; + } + } + + inline std::ostream& operator<< (std::ostream& os, const MediaFormat::e& format) + { + return os << to_string(format); + } +} + +class media_type +{ +public: + media_type() : _ptr(NULL) + { + } + + media_type(IMFMediaType* ptr) : _ptr(ptr) + { + } + + media_type(comet::com_ptr ptr) : _ptr(ptr) + { + } + + cv::Size resolution() const + { + UINT32 width, height; + MFGetAttributeSize(_ptr.in(), MF_MT_FRAME_SIZE, &width, &height) | comet::raise_exception; + return cv::Size(width, height); + } + + double framerate() const + { + UINT32 num, denom; + MFGetAttributeRatio(_ptr.in(), MF_MT_FRAME_RATE, &num, &denom) | comet::raise_exception; + return static_cast(num) / denom; + } + + MediaFormat::e format() const + { + GUID subtype; + _ptr->GetGUID(MF_MT_SUBTYPE, &subtype) | comet::raise_exception; + return MediaFormat::fromGUID(subtype); + } + +#ifdef _TYPEDEF_BOOL_TYPE + typedef media_type _Myt; + _TYPEDEF_BOOL_TYPE; + _OPERATOR_BOOL() const _NOEXCEPT + { // test for non-null pointer + return (_ptr != 0 ? _CONVERTIBLE_TO_TRUE : 0); + } +#else + explicit operator bool() const + { // test for non-null pointer + return _ptr != 0; + } +#endif + + comet::com_ptr _ptr; +}; + + +class camera +{ +public: + camera() + { + } + + camera(IMFActivate* ptr) : activate_ptr(ptr) + { + } + + camera(comet::com_ptr ptr) : activate_ptr(ptr) + { + } + + // TODO actually implement these or make sure they are deleted/created correctly, this might be the reason for weird mapping of stuff + camera(const camera& other) + { +// this-> +// this = other; + } + //= delete; + + camera(camera&& other) + { + *this = std::move(other); + } + + //camera& operator=(const camera& other) = delete; + + camera& operator=(camera&& other) + { + activate_ptr.swap(other.activate_ptr); + source_ptr.swap(other.source_ptr); + reader_ptr.swap(other.reader_ptr); + return *this; + } + + ~camera() + { + shutdown(); + } + + bool is_active() const + { + return !source_ptr.is_null(); + } + + void activate() + { + if (activate_ptr) + activate_ptr->ActivateObject(IID_IMFMediaSource, reinterpret_cast(source_ptr.out())) | comet::raise_exception; + reader_ptr = NULL; + } + + void shutdown() + { + if (activate_ptr) + activate_ptr->ShutdownObject() | comet::raise_exception; + source_ptr = NULL; + reader_ptr = NULL; + } + + cv::Mat read_frame(int streamIndex = MF_SOURCE_READER_FIRST_VIDEO_STREAM, int bufferIndex = 0) + { + if (!activate_ptr) + return cv::Mat(); + + if (!reader_ptr) + { + comet::com_ptr pAttributes; + MFCreateAttributes(pAttributes.out(), 1) | comet::raise_exception; + pAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, TRUE) | comet::raise_exception; + pAttributes->SetUINT32(MF_SOURCE_READER_DISCONNECT_MEDIASOURCE_ON_SHUTDOWN, TRUE) | comet::raise_exception; + + MFCreateSourceReaderFromMediaSource(source_ptr.in(), pAttributes.in(), reader_ptr.out()) | comet::raise_exception; + } + + comet::com_ptr sample; + DWORD actualStreamIndex, flags; + LONGLONG timestamp; + try + { + do + { + reader_ptr->ReadSample( + streamIndex, // Stream index. + 0, // Flags. + &actualStreamIndex, // Receives the actual stream index. + &flags, // Receives status flags. + ×tamp, // Receives the time stamp. + sample.out() // Receives the sample or NULL. + ) | comet::raise_exception; + } while (sample == NULL && (flags & MF_SOURCE_READERF_STREAMTICK)); + } + catch (std::exception& e) + { + std::cerr << "Error getting frame: " << e.what() << std::endl; + std::cerr << " flags: " << flags << std::endl; + shutdown(); + activate(); + throw; + } + + media_type cur_media_type; + reader_ptr->GetCurrentMediaType(actualStreamIndex, cur_media_type._ptr.out()) | comet::raise_exception; + + //PrintAttributes(cur_media_type._ptr.in()); + + auto format = cur_media_type.format(); + + cv::Mat ret; + + DWORD bufCount; + sample->GetBufferCount(&bufCount) | comet::raise_exception; + + DWORD bufIndex = bufferIndex >= 0 ? bufferIndex : bufCount - bufferIndex; + + if (bufCount > bufIndex) + { + comet::com_ptr buffer; + sample->GetBufferByIndex(bufferIndex, buffer.out()) | comet::raise_exception; + + switch (format) + { + case MediaFormat::RGB24: + case MediaFormat::ARGB32: + case MediaFormat::RGB32: + { + comet::com_ptr buf2d = try_cast(buffer); + + //DWORD length; + //buf2d->GetContiguousLength(&length) | comet::raise_exception; + + //ret.create(); + + //COMET_ASSERT(ret.dataend - ret.datastart == length); + + auto resolution = cur_media_type.resolution(); + + struct buf2d_lock + { + comet::com_ptr& buf2d; + + buf2d_lock(comet::com_ptr& buf2d, BYTE*& scanline0, LONG& pitch) : buf2d(buf2d) + { + buf2d->Lock2D(&scanline0, &pitch) | comet::raise_exception; + } + + ~buf2d_lock() + { + buf2d->Unlock2D() | comet::raise_exception; + } + }; + + BYTE *scanline0; + LONG pitch; + buf2d_lock buf_lock_token(buf2d, scanline0, pitch); + if (pitch >= 0) + { + cv::Mat buf2dmat(resolution, + (format == MediaFormat::RGB24) ? CV_8UC3 : CV_8UC4, + scanline0, + pitch); + buf2dmat.copyTo(ret); + } + else + { + cv::Mat buf2dmat(resolution, + (format == MediaFormat::RGB24) ? CV_8UC3 : CV_8UC4, + scanline0 + pitch*(resolution.height-1), + -pitch); + cv::flip(buf2dmat, ret, 0); + } + + break; + } + case MediaFormat::MJPG: + { + struct buf_lock + { + comet::com_ptr& buffer; + + buf_lock(comet::com_ptr& buffer, BYTE*& data, DWORD& maxLength, DWORD& length) : buffer(buffer) + { + buffer->Lock(&data, &maxLength, &length) | comet::raise_exception; + } + + ~buf_lock() + { + buffer->Unlock() | comet::raise_exception; + } + }; + + BYTE* data; + DWORD length; + DWORD maxLength; + + buf_lock buf_lock_token(buffer, data, maxLength, length); + + ret = cv::imdecode(cv::Mat(length, 1, CV_8U, data), cv::IMREAD_COLOR); + + break; + } + default: + std::stringstream sstream; + sstream << "Unknown media format: " << format; + throw std::runtime_error(sstream.str()); + } + } + + return ret; + } + + std::string name() const + { + return get_attr_str(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME); + } + + std::string symlink() const + { + return get_attr_str(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK); + } + + // TODO change + //explicit operator bool() const + //{ + // return !activate_ptr.is_null(); + //} + + enum Property + { + // CameraControl + Exposure, + Focus, + Zoom, + Pan, + Tilt, + Roll, + Iris, + + // VideoProcAmp + Brightness, + Contrast, + Hue, + Saturation, + Sharpness, + Gamma, + ColorEnable, + WhiteBalance, + BacklightCompensation, + Gain + }; + + bool has(Property property) const; + cam_prop_range get_range(Property property) const; + cam_prop_value get(Property property) const; + void set(Property property, const cam_prop_value& value); + + static std::vector list_properties(); + static const char* property_name(Property); + + std::vector media_types(int streamIndex = 0) const + { + auto pHandler = getMediaTypeHandler(streamIndex); + + DWORD cTypes = 0; + pHandler->GetMediaTypeCount(&cTypes) | comet::raise_exception; + + std::vector ret; + for (DWORD i = 0; i < cTypes; i++) + { + comet::com_ptr pType; + pHandler->GetMediaTypeByIndex(i, pType.out()) | comet::raise_exception; + ret.emplace_back(pType); + } + + return ret; + } + + media_type get_media_type(int streamIndex = 0) + { + media_type ret; + getMediaTypeHandler(streamIndex)->GetCurrentMediaType(ret._ptr.out()) | comet::raise_exception; + return ret; + } + + void set_media_type(const media_type& type, int streamIndex = 0) + { + getMediaTypeHandler(streamIndex)->SetCurrentMediaType(type._ptr.in()) | comet::raise_exception; + if (reader_ptr) + { + reader_ptr->SetCurrentMediaType(streamIndex, nullptr, type._ptr.in()) | comet::raise_exception; + } + } + + // TODO change + //explicit operator bool() { + // return !activate_ptr.is_null(); + //} + +private: + std::string get_attr_str(REFGUID guid) const + { + comet::task_ptr pStr; + UINT32 strLen; + activate_ptr->GetAllocatedString(guid, pStr.out(), &strLen) | comet::raise_exception; + return comet::bstr_t(pStr.in(), strLen).s_str(); + } + + comet::com_ptr getMediaTypeHandler(int streamIndex = 0) const + { + comet::com_ptr pPD; + source_ptr->CreatePresentationDescriptor(pPD.out()) | comet::raise_exception; + + BOOL fSelected; + comet::com_ptr pSD; + pPD->GetStreamDescriptorByIndex(streamIndex, &fSelected, pSD.out()) | comet::raise_exception; + + comet::com_ptr pHandler; + pSD->GetMediaTypeHandler(pHandler.out()) | comet::raise_exception; + + return pHandler; + } + + comet::com_ptr activate_ptr; + comet::com_ptr source_ptr; + comet::com_ptr reader_ptr; +}; + +class camera_helper +{ +public: + static std::vector get_all_cameras() + { + comet::com_ptr config; + MFCreateAttributes(config.out(), 1) | comet::raise_exception; + + config->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID) | comet::raise_exception; + + comet::com_ptr_array com_ptr_array; + MFEnumDeviceSources(config.in(), com_ptr_array.out(), com_ptr_array.out_count()) | comet::raise_exception; + + std::vector ret; + for (size_t i = 0; i < com_ptr_array.count(); ++i) + { + ret.emplace_back(com_ptr_array[i]); + } + return ret; + } + + static camera get_camera_by_symlink(const std::string& symlink) + { + // This is how you should do it, but for some reason it gives an activate pointer with no friendly name + + // comet::com_ptr config; + // MFCreateAttributes(config.out(), 1) | comet::raise_exception; + // + // config->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID) | comet::raise_exception; + // comet::bstr_t symlink_bstr(symlink); + // config->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, symlink_bstr.c_str()) | comet::raise_exception; + // + // comet::com_ptr activate_ptr; + // MFCreateDeviceSourceActivate(config.in(), activate_ptr.out()) | comet::raise_exception; + // + // return camera(activate_ptr); + + for(auto&& camera : get_all_cameras()) + { + if (camera.symlink() == symlink) + return std::move(camera); + } + + throw std::runtime_error("No camera with symlink: " + std::string(symlink)); + } +}; + +#endif // camera_helper_h__ diff --git a/lib/local/CamCom/comet/array.h b/lib/local/CamCom/comet/array.h new file mode 100644 index 00000000..f305f746 --- /dev/null +++ b/lib/local/CamCom/comet/array.h @@ -0,0 +1,150 @@ +/** \file +* Array wrapper. +*/ +/* +* Copyright 2001 Sofus Mortensen +* +* This material is provided "as is", with absolutely no warranty +* expressed or implied. Any use is at your own risk. Permission to +* use or copy this software for any purpose is hereby granted without +* fee, provided the above notices are retained on all copies. +* Permission to modify the code and to distribute modified code is +* granted, provided the above notices are retained, and a notice that +* the code was modified is included with the above copyright notice. +* +* This header is part of Comet version 2. +* https://github.com/alamaison/comet +* +*/ + +/* +* comet::array_t is adapted from class array by Nicolai M. Josuttis +* +* (C) Copyright Nicolai M. Josuttis 2001. +* Permission to copy, use, modify, sell and distribute this software +* is granted provided this copyright notice appears in all copies. +* This software is provided "as is" without express or implied +* warranty, and with no claim as to its suitability for any purpose. +* +*/ + +#ifndef COMET_ARRAY_H +#define COMET_ARRAY_H + +#include +#include +#include +#include +#include + +namespace comet { + +#pragma pack(push) +#pragma pack(1) + + /*!\addtogroup Misc + */ + //@{ + + template class array_t + { + T a_[SZ]; + public: + typedef T value_type; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + + typedef typename std::vector::reverse_iterator reverse_iterator; + typedef typename std::vector::const_reverse_iterator const_reverse_iterator; + + typedef typename std::vector::size_type size_type; + typedef typename std::vector::difference_type difference_type; + + typedef T& reference; + typedef const T& const_reference; + + // reference operator[](size_type i) { return a_[i]; } + // const_reference operator[](size_type i) const { return a_[i]; } + + iterator begin() { return iterator(a_); } + iterator end() { return iterator(a_ + SZ); } + const_iterator begin() const { return const_iterator(a_); } + const_iterator end() const { return const_iterator(a_ + SZ); } + + reverse_iterator rbegin() { return reverse_iterator(a_); } + reverse_iterator rend() { return reverse_iterator(a_ + SZ); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(a_); } + const_reverse_iterator rend() const { return const_reverse_iterator(a_ + SZ); } + + operator const T*() const { return a_; } + operator T*() { return a_; } + + static size_type size() { return SZ; } + static bool empty() { return false; } + static size_type max_size() { return SZ; } + enum { static_size = SZ }; + + reference front() { return a_[0]; } + const_reference front() const { return a_[0]; } + reference back() { return a_[SZ-1]; }; + const_reference back() const { return a_[SZ-1]; } + + // swap (note: linear complexity) + void swap (array_t& y) { + std::swap_ranges(begin(),end(),y.begin()); + } + + // assignment with type conversion + template + array_t& operator= (const array_t& rhs) { + std::copy(rhs.begin(),rhs.end(), begin()); + return *this; + } + + // assign one value to all elements + void assign (const T& value) + { + std::fill_n(begin(),size(),value); + } + + reference at(size_type i) { rangecheck(i); return a_[i]; } + const_reference at(size_type i) const { rangecheck(i); return a_[i]; } + + private: + // check range (may be private because it is static) + static void rangecheck (size_type i) { + if (i >= size()) { throw std::range_error("array"); } + } + + }; + //@} +#pragma pack(pop) + + // comparisons + template + bool operator== (const array_t& x, const array_t& y) { + return std::equal(x.begin(), x.end(), y.begin()); + } + template + bool operator< (const array_t& x, const array_t& y) { + return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end()); + } + template + bool operator!= (const array_t& x, const array_t& y) { + return !(x==y); + } + template + bool operator> (const array_t& x, const array_t& y) { + return y + bool operator<= (const array_t& x, const array_t& y) { + return !(y + bool operator>= (const array_t& x, const array_t& y) { + return !(x + +# if defined(COMET_ASSERT_THROWS) || defined(COMET_ASSERT_THROWS_ALWAYS) +# if defined(COMET_ASSERT_THROWS_ALWAYS) || !defined(NDEBUG) +namespace comet +{ + /*! \defgroup ErrorHandling Error handling. + */ + //@{ +//! Indicates a comet assertion failed. +/** This is enabled for debug builds if COMET_ASSERT_THROWS is defined and + * enabled for both debug and release if COMET_ASSERT_THROWS_ALWAYS is defined. + */ +struct assert_failed : std::runtime_error +{ + assert_failed( const char *val) : runtime_error(val) {} +}; + //@} +} +# define COMET_ASSERT(x_) if (x_) ; else throw comet::assert_failed("Assert Failed: " #x_ ); +# define COMET_THROWS_ASSERT throw( comet::assert_failed) +# else +# define COMET_ASSERT(x_) ; +# endif + +# else +# define COMET_THROWS_ASSERT throw() +# ifndef __CYGWIN__ + +# include +# define COMET_ASSERT _ASSERTE + +# else + +# include +# define COMET_ASSERT assert +# endif +# endif +/*! \addtogroup ErrorHandling + */ +//@{ +/** \def COMET_ASSERT Assertion in commet. + * \sa COMET_THROWS_ASSERT COMET_ASSERT_THROWS_ALWAYS + */ + //@} + +#endif diff --git a/lib/local/CamCom/comet/atl_module.h b/lib/local/CamCom/comet/atl_module.h new file mode 100644 index 00000000..5c37bdcb --- /dev/null +++ b/lib/local/CamCom/comet/atl_module.h @@ -0,0 +1,121 @@ +/** \file + * ATL Module Compatability wrapper. + */ +/* + * Copyright 2000, 2001 Paul Hollingsworth, Michael Geddes + * Copyright 2013 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_ATL_MODULE_H +#define COMET_ATL_MODULE_H + +#include + +#include + +namespace comet { + /*! \defgroup ATL ATL conversion. + */ + //@{ + + /** \struct declspec_comtype atl_module.h comet/atl_module.h + * Helper to create a 'comtype' for a non-comet interface. + * \code + * namespace comet{ + * template<> struct comtype : + * declspec_comtype + * { }; + * }; + * \endcode + * + */ + template struct declspec_comtype + { + static const uuid_t& uuid() { return uuid_t::create_const_reference(__uuidof(ITF)); } + typedef BASE base; + }; + + + /// Placeholder for an empty comet typelibrary. + struct empty_typelib + { + typedef nil coclasses; + }; + + template + class atl_module_ex : public ATL_MODULE + { + // ATL will take the responsibility of registering the embedded type library. + public: + HRESULT Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, const GUID* plibid = NULL) + { + module().instance(h); + return ATL_MODULE::Init(p, h, plibid); + } + + HRESULT RegisterServer(BOOL bRegTypeLib = FALSE, const CLSID* pCLSID = NULL) + { + HRESULT hr = ATL_MODULE::RegisterServer(bRegTypeLib, pCLSID); + if(SUCCEEDED(hr)) + hr = COM_SERVER::DllRegisterServer(); + return hr; + } + + HRESULT UnregisterServer(BOOL bUnRegTypeLib, const CLSID* pCLSID = NULL) + { + COM_SERVER::DllUnregisterServer(); + return ATL_MODULE::UnregisterServer(bUnRegTypeLib, pCLSID); + } + + HRESULT GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) + { + HRESULT hr = COM_SERVER::DllGetClassObject( rclsid, riid, ppv); + if( hr == CLASS_E_CLASSNOTAVAILABLE ) + { + hr = ATL_MODULE::GetClassObject(rclsid, riid,ppv); + } + return hr; + } + + LONG GetLockCount() + { + return module().rc() + ATL_MODULE::GetLockCount(); + } + }; + + /** Wraps an ATL::CComModule to provide co-existing ATL/Comet co-classes. + * ATL will take responsibility for registering the embedded type-library. + * + * Here is an example of how to use it: + * \code + * struct typelib_subset + * { + * typedef COMET_STRICT_TYPENAME comet::typelist::make_list< CoClass1, CoClass2 > coclasses; + * }; + * comet::atl_module _Module; + * \endcode + * And in std.h: + * \code + * struct typelib_subset; + * extern comet::atl_module _Module; + * \endcode + */ + template + class atl_module : public atl_module_ex< com_server >, ATL_MODULE > + { + }; + //@} +} // namespace comet + +#endif diff --git a/lib/local/CamCom/comet/auto_buffer.h b/lib/local/CamCom/comet/auto_buffer.h new file mode 100644 index 00000000..0a304bd1 --- /dev/null +++ b/lib/local/CamCom/comet/auto_buffer.h @@ -0,0 +1,99 @@ +/** \file + * Simple uncopyable buffer class. + */ + /* + * Copyright 2004, Michael Geddes, Lijun Qin. + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef INCLUDE_COMET_AUTO_BUFFER_H +#define INCLUDE_COMET_AUTO_BUFFER_H + +#ifdef _SHOW_INC +#pragma message(" #Include " __FILE__) +#endif + +namespace comet +{ + /*!\addtogroup Misc + */ + //@{ + + /** \class auto_buffer_t auto_buffer.h comet/auto_buffer.h + * Simle auto-deleting buffer class. + * Non-copyable /returnable. + */ + template + class auto_buffer_t + { + public: + typedef size_t size_type; + /** Create a buffer of the given size. + * This is the only valid constructor. + */ + explicit auto_buffer_t(size_type size) + { + begin_ = new T[size]; + } + /// Auto-delete the buffer. + ~auto_buffer_t() throw() { delete[] begin_; } + + /// Clear the buffer. + void clear() throw() + { + delete[] begin_; + begin_ = 0; + } + /// Is the buffer empty. + bool empty() const throw() + { + return begin_ != NULL; + } + /// Resize the buffer. + void resize( size_type newSize) throw() + { + delete[] begin_; + begin_ = new T[newSize]; + } + + /// Return a reference to the specifed element. + T & at( size_type t) throw() { return begin_[t]; } + T & operator[]( size_type t) throw() { return begin_[t]; } + /// Return a const reference to the specifed element. + const T & at( size_type t) const throw() { return begin_[t]; } + const T & operator[]( size_type t) const throw() { return begin_[t]; } + + /// Detatch the memory. + T *detach() + { + T *val = begin_; + begin_ = NULL; + return val; + } + /// Return the memory. + T *get() { return begin_; } + const T *get()const { return begin_; } + + private: + /// Can't assign. + auto_buffer_t &operator=(const auto_buffer_t &); + /// can't copy. + auto_buffer_t(const auto_buffer_t&); + + /// Pointer to memory. + T *begin_; + }; + //@} +} + +#endif /* INCLUDE_COMET_AUTO_BUFFER_H */ diff --git a/lib/local/CamCom/comet/bstr.h b/lib/local/CamCom/comet/bstr.h new file mode 100644 index 00000000..23fd332d --- /dev/null +++ b/lib/local/CamCom/comet/bstr.h @@ -0,0 +1,948 @@ +/** \file + * BSTR wrapper classes. + */ +/* + * Copyright 2000-2004 Sofus Mortensen, Michael Geddes + * Copyright 2012 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_BSTR_H +#define COMET_BSTR_H + +#include + +#ifdef COMET_BROKEN_WTYPES +#include +#endif // COMET_BROKEN_WTYPES +#include +#include +#include +#ifndef COMET_GCC_HEADERS +#include +#endif // COMET_GCC_HEADERS + +#include +#include +#undef max +#include + +#include +#include +#include +#include +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable : 4522 4521) + +#pragma comment( lib, "oleaut32" ) + +namespace comet { + +#ifndef NORM_IGNOREKASHIDA +#define NORM_IGNOREKASHIDA 0x00040000 +#endif // NORM_IGNOREKASHIDA + /*! \addtogroup COMType + */ + //@{ + //! Comparsion flags. + /*! Can be used with \link comet::bstr_t::cmp cmp \endlink or the comparsion functors. + \sa cmp + less + less_equal + greater + greater_equal + equal_to + not_equal_to + */ + + enum compare_flags_t + { + cf_ignore_case = NORM_IGNORECASE, //!< Ignore case. + cf_ingore_nonspace = NORM_IGNORENONSPACE, //!< Ignore nonspacing chars. + cf_ignore_symbols = NORM_IGNORESYMBOLS, //!< Ignore symbols. + cf_ignore_width = NORM_IGNOREWIDTH, //!< Ignore string width. + cf_ignore_kanatype = NORM_IGNOREKANATYPE, //!< Ignore Kana type. + cf_ignore_kashida = NORM_IGNOREKASHIDA //!< Ignore Arabic kashida characters. + }; + //@} + + namespace impl { + + inline const wchar_t* null_to_empty(const wchar_t* s) + { return s ? s : L""; } + + } // namespace + + + /*! \addtogroup COMType + */ + //@{ + + /*! \class bstr_t bstr.h comet/bstr.h + * BSTR Wrapper. + * \sa bstr_t + */ + class bstr_t { + public: + typedef wchar_t value_type; +#if !(defined(_STLP_DEBUG) || (defined(_HAS_ITERATOR_DEBUGGING)) && _MSC_VER >= 1400) + typedef std::wstring::iterator iterator; + typedef std::wstring::const_iterator const_iterator; + + typedef std::wstring::reverse_iterator reverse_iterator; + typedef std::wstring::const_reverse_iterator const_reverse_iterator; +#else // _STLP_DEBUG + typedef wchar_t *iterator; + typedef const wchar_t *const_iterator; +#if defined(COMET_STD_ITERATOR) + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; +#else + // workaround for broken reverse_iterator implementations due to no partial specialisation + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; +#endif +#endif // _STLP_DEBUG + + typedef std::wstring::size_type size_type; + typedef std::wstring::difference_type difference_type; + + typedef wchar_t& reference; + typedef const wchar_t const_reference; + + private: + BSTR str_; + + void construct() { str_ = 0; } + void construct(BSTR s, bool copy) throw(std::bad_alloc) + { if (copy) str_ = copy_str(s); else str_ = s; } + + void construct(const wchar_t* s) throw(std::bad_alloc) + { str_ = copy_str(s); } + + void construct(const wchar_t* s, size_t len) throw(std::bad_alloc) + { str_ = copy_str(s, len); } + + void construct(const char* s) throw(std::bad_alloc, std::runtime_error) { + convert_str(s, -1); + } + + void construct(const char* s, size_t len) + { + if (len >= static_cast(std::numeric_limits::max())) + throw std::length_error( + "String exceeded maximum length for conversion"); + + convert_str(s, static_cast(len+1)); + } + + void construct(const uuid_t& u, bool braces) + { + str_ = impl::bad_alloc_check(::SysAllocStringLen(0, braces?38:36)); + u.copy_to_str(str_+(braces?1:0)); + if (braces) + { + str_[0]=L'{'; + str_[37]=L'}'; + } + } + + void construct(const wchar_t* s1, size_t l1, const wchar_t* s2, size_t l2) throw(std::bad_alloc) + { + str_ = impl::bad_alloc_check(::SysAllocStringLen(NULL, UINT(l1+l2))); + if (l1) memcpy(str_, s1, sizeof(wchar_t)*(l1)); + if (l2) memcpy(str_+l1, s2, sizeof(wchar_t)*(1+l2)); + } + + void destroy() throw() + { if (str_) ::SysFreeString(str_); } + + bool is_regular() const throw() + { return !str_ || length() == wcslen(str_); } + + static BSTR copy_str(const wchar_t* src) throw(std::bad_alloc) + { return src ? impl::bad_alloc_check(::SysAllocString(src)) : 0; } + + static BSTR copy_str(const wchar_t* src, size_t len) throw(std::bad_alloc) + { return src ? impl::bad_alloc_check(::SysAllocStringLen(src, UINT(len))) : 0; } + + static BSTR copy_str(BSTR src) throw(std::bad_alloc) + { return src ? impl::bad_alloc_check(::SysAllocStringLen(src, ::SysStringLen(src))) : 0; } + + void convert_str(const char* s, int l) throw(std::bad_alloc, std::runtime_error) + { + if (s != 0) { +#if defined(_MBCS) || !defined(COMET_NO_MBCS) + int wl = ::MultiByteToWideChar(CP_ACP, 0, s, l, NULL,0); +#else + int wl = ((l>=0)?l: (1+strlen(s))); + COMET_ASSERT( wl == ::MultiByteToWideChar( CP_ACP, 0, s, l, NULL,0)); +#endif + str_ = impl::bad_alloc_check(::SysAllocStringLen(0, wl - 1)); + if (::MultiByteToWideChar(CP_ACP, 0, s, l, str_, wl) == 0) + { + destroy(); + throw std::runtime_error("MultiByteToWideChar has failed"); + } + } else str_ = 0; + } + + public: + /*! Default constructor + Constructs a null string. + */ + bstr_t() throw() + { + construct(); + } + + //! Copy constructor + /*! + \param s + String initialise bstr_t from. + + \exception std::bad_alloc + On memory exhaustion std::bad_alloc is thrown. + */ + bstr_t(const bstr_t& s) throw(std::bad_alloc) + { + construct(s.str_, true); + } + + //! Construct string from const wchar_t* + /*! + \param s + String to initialise bstr_t from. + + \exception std::bad_alloc + On memory exhaustion std::bad_alloc is thrown. + */ + bstr_t(const wchar_t* s) throw(std::bad_alloc) + { + construct(s); + } + + bstr_t(const wchar_t* s, size_t len) throw(std::bad_alloc) + { + construct(s, len); + } + + //! Construct string from const char* + /*! + \param s + String to initialise bstr_t from. + + \exception std::bad_alloc + On memory exhaustion std::bad_alloc is thrown. + \exception std::runtime_error + Should string conversion fail, std::runtime_error will be thrown. + */ + bstr_t(const char* s) throw(std::runtime_error) + { + construct(s); + } + + bstr_t(const char* s, size_t len) throw(std::bad_alloc) + { + construct(s, len); + } + + //! Construct string from const std::string& + /*! + \param s + String to initialise bstr_t from. + + \exception std::bad_alloc + On memory exhaustion std::bad_alloc is thrown. + \exception std::length_error + If this given string is too long to be converted, + std::length_error is thrown. + \exception std::runtime_error + Should string conversion fail, std::runtime_error is thrown. + */ + bstr_t(const std::string& s) + { + construct(s.c_str(), s.length()); + } + + //! Construct string from BSTR + /*! + Takes ownership of specified BSTR. To prevent misuse the BSTR must be wrapped using auto_attach. + + \code + bstr_t bs( auto_attach( myBSTR ) ); + \endcode + + \param s + String to initialise bstr_t from. + */ + bstr_t(const impl::auto_attach_t& s) throw() + { + construct(s.get(), false); + } + + + //! Construct string from const std::wstring& + /*! + \param s + String to initialise bstr_t from. + + \exception std::bad_alloc + On memory exhaustion std::bad_alloc is thrown. + */ + bstr_t(const std::wstring& s) throw(std::bad_alloc) + { + construct(s.c_str(), s.length()); + } + + explicit bstr_t(size_type sz, wchar_t c) throw(std::bad_alloc) + { + str_ = impl::bad_alloc_check(::SysAllocStringLen(0, UINT(sz))); + std::fill(begin(), end(), c); + } + + explicit bstr_t(size_type sz) throw(std::bad_alloc) + { + str_ = impl::bad_alloc_check(::SysAllocStringLen(0, UINT(sz))); + } + + template + explicit bstr_t(IT first, IT last) + { + str_ = 0; + assign(first, last); + } + + explicit bstr_t(const uuid_t& u, bool braces = false) + { + construct(u, braces); + } + + private: + bstr_t(const wchar_t* s1, size_t l1, const wchar_t* s2, size_t l2) throw(std::bad_alloc) + { + construct(s1, l1, s2, l2); + } + + public: + //! Destructor + /*! + Deletes the wrapped BSTR. + */ + ~bstr_t() throw() + { + destroy(); + } + + //! Swap + void swap(bstr_t& x) throw() + { + std::swap(str_, x.str_); + } + + //! Explicit conversion to const wchar_t* + const wchar_t* c_str() const throw() + { return impl::null_to_empty(str_); } + + //! Explicit conversion to std::wstring + std::wstring w_str() const throw() + { return impl::null_to_empty(str_); } + +#ifdef _MBCS +#ifndef COMET_NO_MBCS_WARNING +#pragma message( "Warning: _MBCS is defined. bstr_t::s_str may return an std::string containing multibyte characters" ) +#endif +#endif + + //! Explicit conversion to std::string + std::string s_str() const + { + if (is_empty()) return std::string(); + + if (length() > static_cast(std::numeric_limits::max())) + throw std::length_error("String is too large to be converted"); + + int ol = static_cast(length()); + +#if defined(_MBCS) || !defined(COMET_NO_MBCS) + // Calculate the required length of the buffer + int l = WideCharToMultiByte(CP_ACP, 0, str_, ol, NULL, 0, NULL, NULL); +#else // _MBCS + int l = ol; + COMET_ASSERT( l == WideCharToMultiByte(CP_ACP, 0, str_, ol, NULL, 0, NULL, NULL)); +#endif // _MBCS + + // Create the buffer + std::string rv(l, std::string::value_type()); + // Do the conversion. + if (0 == WideCharToMultiByte( + CP_ACP, 0, str_, ol, &rv[0], l, NULL, NULL)) + { + DWORD err = GetLastError(); + raise_exception(HRESULT_FROM_WIN32(err)); + } + + return rv; + } + + //! Explicit conversion to "tstring". +#ifdef _UNICODE + std::wstring t_str() const + { + return w_str(); + } +#else + std::string t_str() const + { + return s_str(); + } +#endif + + //! Implicit conversion to std::wstring + operator std::wstring() const { return w_str(); } + + //! Implicit conversion to std::string + operator std::string() const { return s_str(); } + + //! Returns true if and only if wrapped str is null + bool is_null() const throw() + { return str_ == 0; } + + /** Returns true if and only if wrapped str has zero length. + */ + bool is_empty() const throw() { return length() == 0; } + + //! Returns true if and only if wrapped str has zero length. + bool empty() const throw() { return length() == 0; } + + //! Returns length of wrapped string. + size_t length() const throw() + { return is_null() ? 0 : ::SysStringLen(str_); } + + size_t size() const throw() + { return length(); } + + /*! \internal */ + BSTR get_raw() const + { return str_; } + + friend + std::basic_ostream &operator<<(std::basic_ostream &os, const bstr_t &val) + { os << val.s_str(); return os; } + + friend + std::basic_ostream &operator<<(std::basic_ostream &os, const bstr_t &val) + { os << val.w_str(); return os; } + + /// \name Boolean operators + //@{ + + bool operator==(const wchar_t* s) const + { return 0 == wcscmp(c_str(), impl::null_to_empty(s) ) && is_regular(); } + + bool operator!=(const wchar_t* s) const + { return !operator==(s); } + + bool operator<(const wchar_t* s) const + { return wcscmp(c_str(), impl::null_to_empty(s)) < 0 && is_regular(); } + + bool operator>(const wchar_t* s) const + { return wcscmp(c_str(), impl::null_to_empty(s)) > 0 && !is_regular(); } + + bool operator>=(const wchar_t* s) const + { return !operator<(s); } + + bool operator<=(const wchar_t* s) const + { return !operator>(s); } + + bool operator==(const std::wstring& s) const + { + size_t l = length(); + if (l != s.length()) return false; + return 0 == memcmp(str_, s.c_str(), sizeof(wchar_t)*l); + } + + bool operator!=(const std::wstring& s) const + { return !operator==(s); } + + bool operator<(const std::wstring& s) const + { return std::lexicographical_compare(str_, str_+length(), s.begin(), s.end()); } + + bool operator>(const std::wstring& s) const + { return std::lexicographical_compare(str_, str_+length(), s.begin(), s.end(), std::greater()); } + + bool operator>=(const std::wstring& s) const + { return !operator<(s); } + + bool operator<=(const std::wstring& s) const + { return !operator>(s); } + + bool operator==(const bstr_t& s) const + { + if (str_ == 0 && s.str_ == 0) return true; + return ::VarBstrCmp(str_, s.str_, ::GetThreadLocale(), 0) == VARCMP_EQ; + } + + bool operator!=(const bstr_t& s) const + { return !operator==(s); } + + bool operator<(const bstr_t& s) const + { + if (str_ == 0) { + return s.str_ != 0; + } + + if (s.str_ == 0) return false; + + return ::VarBstrCmp(str_, s.str_, ::GetThreadLocale(), 0) == VARCMP_LT; + } + + bool operator>(const bstr_t& s) const + { + if (str_ == 0) { + return s.str_ != 0; + } + + if (s.str_ == 0) return false; + + return ::VarBstrCmp(str_, s.str_, ::GetThreadLocale(), 0) == VARCMP_GT; + } + + bool operator>=(const bstr_t& s) const + { return !operator<(s); } + + bool operator<=(const bstr_t& s) const + { return !operator>(s); } + //@} + + //! String comparsion function. + /*! \param s String to compare + \param flags Comparison Flags + \retval <0 if less + \retval 0 if Equal + \retval >0 if greater + */ + int cmp(const bstr_t& s, compare_flags_t flags = compare_flags_t(0)) const + { + HRESULT res = ::VarBstrCmp(str_, s.str_, ::GetThreadLocale(), flags); + switch(res) + { + case VARCMP_EQ: return 0; + case VARCMP_GT: return 1; + case VARCMP_LT: return -1; + case VARCMP_NULL: + return ((str_==0)?0:1) - ((s.str_==0)?0:1); + } + if (str_==0 || s.str_ ==0) + return ((str_==0)?0:1) - ((s.str_==0)?0:1); + raise_exception(res); return 0; + } + + //!\name Comparison Functors + //@{ + //! Less Functor. + /*! Useful for STL containers. + \code + typedef stl::map < comet::bstr_t, long, bstr_t::less > string_long_map; + \endcode + \param CF comparison flags. + \relates bstr_t + */ + template + struct less : std::binary_function< bstr_t,bstr_t,bool>{ + /// Functor. + bool operator()(const bstr_t& l, const bstr_t& r) const + { return l.cmp(r, CF) <0; } + }; + + //! less or equal functor. + /*! \relates bstr_t */ + template + struct less_equal : std::binary_function< bstr_t,bstr_t,bool> { + /// Functor. + bool operator()(const bstr_t& l, const bstr_t& r) const + { return l.cmp(r, CF) <=0; } + }; + + //! greater functor. + /*! \relates bstr_t */ + template + struct greater : std::binary_function< bstr_t,bstr_t,bool> { + /// Functor. + bool operator()(const bstr_t& l, const bstr_t& r) const + { return l.cmp(r, CF) > 0; } + }; + + //! greater or equal functor. + /*! \relates bstr_t */ + template + struct greater_equal : std::binary_function< bstr_t,bstr_t,bool> { + /// Functor. + bool operator()(const bstr_t& l, const bstr_t& r) const + { return l.cmp(r, CF) >=0; } + }; + + //! equality functor. + template + struct equal_to : std::binary_function< bstr_t,bstr_t,bool> { + bool operator()(const bstr_t& l, const bstr_t& r) const + { return l.cmp(r, CF) == 0; } + }; + + //! Inequality functor. + /*! \relates bstr_t */ + template + struct not_equal_to : std::binary_function< bstr_t,bstr_t,bool>{ + /// Functor. + bool operator()(const bstr_t& l, const bstr_t& r) const + { return l.cmp(r, CF) != 0; } + }; + //@} + + iterator begin() { return iterator(str_); } + iterator end() { return iterator(str_ + length()); } + const_iterator begin() const { return const_iterator(str_); } + const_iterator end() const { return const_iterator(str_ + length()); } + + reverse_iterator rbegin() { return reverse_iterator(str_); } + reverse_iterator rend() { return reverse_iterator(str_ + length()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(str_); } + const_reverse_iterator rend() const { return const_reverse_iterator(str_ + length()); } + + reference at(size_type i) { rangecheck(i); return str_[i]; } + const_reference at(size_type i) const { rangecheck(i); return str_[i]; } + + private: + // check range (may be private because it is static) + void rangecheck (size_type i) const { + if (i >= length()) { throw std::range_error("bstr_t"); } + } + + public: + const_reference operator[](size_type idx) const + { return str_[idx]; } + + reference operator[](size_type idx) + { return str_[idx]; } + + //! Assign string to be \p sz of character \p c . + void assign(size_type sz, wchar_t c) throw(std::bad_alloc) + { + bstr_t t(sz, c); + swap(t); + } + + //! Assign string from two iterators. + template + void assign(IT first, IT last) + { + bstr_t t( std::distance(first, last) ); + +#pragma warning(push) +#pragma warning(disable:4996) + std::copy(first, last, t.begin()); +#pragma warning(pop) + + swap(t); + } + + + //! Assignment operator from any (non integer) constructable. + template + bstr_t& operator=(const T& x) + { + COMET_STATIC_ASSERT( type_traits::is_integer::result == false ); + bstr_t t(x); + swap(t); + return *this; + } + + //! Default assignment. + bstr_t& operator=(const bstr_t& x) throw(std::bad_alloc) + { + bstr_t t(x); + swap(t); + return *this; + } + + //! Concat operation + bstr_t operator+(const bstr_t& s) const throw(std::bad_alloc) + { + return bstr_t(str_, length(), s.str_, s.length()); + } + + //! Concat with const wchar_t* + bstr_t operator+(const wchar_t* s) const throw(std::bad_alloc) + { + return bstr_t(str_, length(), s, wcslen(s)); + } + + //! Concat with std::wstring + bstr_t operator+(const std::wstring& s) const throw(std::bad_alloc) + { + return bstr_t(str_, length(), s.c_str(), s.length()); + } + + //! Concat assignment + bstr_t& operator+=(const bstr_t& s) throw(std::bad_alloc) + { + bstr_t t(str_, length(), s.str_, s.length()); + swap(t); + return *this; + } + + //! Concat assignment with const wchar_t* + bstr_t& operator+=(const wchar_t* s) throw(std::bad_alloc) + { + bstr_t t(str_, length(), s, wcslen(s)); + swap(t); + return *this; + } + + //! Concat assignment with std::wstring + bstr_t& operator+=(const std::wstring& s) throw(std::bad_alloc) + { + bstr_t t(str_, length(), s.c_str(), s.length()); + swap(t); + return *this; + } + + // Detach a raw BSTR from it's wrapper - detach function is dangerous. + BSTR detach() + { + BSTR s(str_); + str_ = 0; + return s; + } + + public: + + //!\name Create a reference to a BSTR + /*! + Creates a bstr_t that is a reference to the BSTR. + It will not be reference counted and will not be deleted when the bstr_t goes out of scope. + + This is used by the interface wrappers for [in] BSTR's. Typically clients do not need create_reference. + */ + //@{ + static const bstr_t& create_const_reference(const BSTR& s) throw() + { return *reinterpret_cast(&s); } + + static bstr_t& create_reference(BSTR& s) throw() + { return *reinterpret_cast(&s); } + //@} + + //! Detaches specified bstr + static BSTR detach(bstr_t& s) + { + return s.detach(); + } + + /*! \internal */ + template + static BSTR detach(const T& s) + { + return bstr_t(s).detach(); + } + + /*! \internal */ + BSTR* get_ptr_to_raw() const + { + return const_cast(&str_); + } + + //! [in] adapter. + /*! + Used when calling raw interfaces that require an [in] BSTR argument. + + \code + bstr_t bs; + HRESULT hr = pRawInterface->raw_Method(bs.in()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + BSTR in() const throw() + { + return str_; + } + + //! [in] adapter. + /*! + Used when calling raw interfaces that require an [in] BSTR* argument. + + \code + bstr_t bs; + HRESULT hr = pRawInterface->raw_Method(bs.in_ptr()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + BSTR* in_ptr() const throw() + { + return const_cast(&str_); + } + + //! [out] adapter. + /*! + Used when calling raw interfaces that require an [out] BSTR * argument. + + \code + bstr_t bs; + HRESULT hr = pRawInterface->raw_MethodThatReturnsBSTR(bs.out()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + BSTR* out() throw() + { + destroy(); + return &str_; + } + + //! [in, out] adapter. + /*! + Used when calling raw interfaces that require an [in, out] BSTR * argument. + + \code + bstr_t bs; + HRESULT hr = pRawInterface->raw_MethodThatChangesBSTR(bs.inout()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + + \note If the wrapped BSTR is shared. The bstr_t is copied so that only this version is modified. + + \exception std::bad_alloc + Throws std::bad_alloc if the bstr_t is being copied and memory is exhausted. + */ + BSTR* inout() throw(std::bad_alloc) + { + return &str_; + } + + friend bstr_t operator+(const std::wstring& s, const bstr_t& t) throw(std::bad_alloc); + friend bstr_t operator+(const wchar_t* s, const bstr_t& t) throw(std::bad_alloc); + }; + + //! Concat operation + inline bstr_t operator+(const std::wstring& s, const bstr_t& t) throw(std::bad_alloc) + { + return bstr_t(s.c_str(), s.length(), t.str_, t.length()); + } + + //! Concat operation + inline bstr_t operator+(const wchar_t* s, const bstr_t& t) throw(std::bad_alloc) + { + return bstr_t(s, wcslen(s), t.str_, t.length()); + } + //@} + + /*! \name Boolean Operators on String + * \relates bstr_t + */ + //@{ + inline bool operator==(const wchar_t* s1, const bstr_t& s2) throw() + { + return s2 == s1; + } + + inline bool operator!=(const wchar_t* s1, const bstr_t& s2) throw() + { + return s2 != s1; + } + + inline bool operator<(const wchar_t* s1, const bstr_t& s2) throw() + { + return s2 > s1; + } + + inline bool operator>(const wchar_t* s1, const bstr_t& s2) throw() + { + return s2 < s1; + } + + inline bool operator<=(const wchar_t* s1, const bstr_t& s2) throw() + { + return s2 >= s1; + } + + inline bool operator>=(const wchar_t* s1, const bstr_t& s2) throw() + { + return s2 <= s1; + } + + inline bool operator==(const std::wstring& s1, const bstr_t& s2) throw() + { + return s2 == s1; + } + + inline bool operator!=(const std::wstring& s1, const bstr_t& s2) throw() + { + return s2 != s1; + } + + inline bool operator<(const std::wstring& s1, const bstr_t& s2) throw() + { + return s2 > s1; + } + + inline bool operator>(const std::wstring& s1, const bstr_t& s2) throw() + { + return s2 < s1; + } + + inline bool operator<=(const std::wstring& s1, const bstr_t& s2) throw() + { + return s2 >= s1; + } + + inline bool operator>=(const std::wstring& s1, const bstr_t& s2) throw() + { + return s2 <= s1; + } + //@} + + + // Implementation of uuid_t construct from bstr. + inline uuid_t::uuid_t(const bstr_t& bs) + { + if (init_from_str(bs.c_str(), bs.length()) == false) throw std::runtime_error(err_msg()); + } + + inline currency_t& currency_t::parse( const bstr_t &str, LCID locale ) + { + VarCyFromStr( str.in(), locale, 0, &cy_ ) | raise_exception; + return *this; + } + +} // namespace + +namespace { + COMET_STATIC_ASSERT( sizeof(comet::bstr_t) == sizeof(BSTR) ); + COMET_STATIC_ASSERT( sizeof(comet::bstr_t) == sizeof(BSTR) ); +} + +namespace std { + template<> inline void swap( comet::bstr_t& x, comet::bstr_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +} + +#include + +#pragma warning(pop) + +#endif /* COMET_BSTR_H */ diff --git a/lib/local/CamCom/comet/calllog.h b/lib/local/CamCom/comet/calllog.h new file mode 100644 index 00000000..0193f9bd --- /dev/null +++ b/lib/local/CamCom/comet/calllog.h @@ -0,0 +1,628 @@ +/** \file + * Provide logging for calls. + */ +/* + * Copyright 2003 Michael Geddes + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_CALLLOG_H +#define COMET_CALLLOG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** \page cometcalllogging Call Logging + This is a feature of the comet interfaces that allows the server to log all + calls to any of the interface methods. It logs parameter values, and also allows + logging of errors (see \ref comethandleexception ). + + Call logging in comet is enabled by using tlb2h -L (see \ref tlb2husage). + + To enable logging to a specified file, define COMET_LOGFILE and define + COMET_LOGFILE_DEFAULT to be the required logfile (see comet::stream_call_logger_t). + + To override logging, specialise comet::call_logger_ and implement all the interfaces + defined by the default comet::call_logger_. + + How various parameter types are output can be modified by specialising the + function comet::comet_log. + */ + +/** \defgroup CallLog Call-logging + *@{ + */ +#ifdef COMET_DOXYGEN // Only for doxygen +/** \def COMET_LOGFILE + * Define to get simple interface logfiles. + * Must use tlb2h -L to enable interface logging. See \ref tlb2husage. + * \sa comet_log comet_log_interface comet_log_array stream_call_logger_t tofstream_comet + */ + +#define COMET_LOGFILE + +/** \def COMET_LOGFILE_DEFAULT + * If COMET_LOGFILE is defined, define to set the default logfile name. + * \code + #define COMET_LOGFILE_DEFAULT "C:\\log\\comet.log" + * \endcode + * \relates call_logger_ + */ +#define COMET_LOGFILE_DEFAULT "C:\\log\\comet.log" +#endif // COMET_DOXYGEN + + //@} + +namespace comet +{ +/*!\addtogroup CallLog + */ +//@{ + /** \struct call_logger_ calllog.h comet/calllog.h + * Default NULL logger and description of 'call logger' concept. + * Specialise to \b true to override. + * \code + template<> + struct call_logger_ : stream_call_logger_t + { + }; + * \endcode + * \sa stream_call_logger_t tofstream_comet + */ + template + struct call_logger_ + { + /** Should the call be logged? + * \return Return true to allow call to be logged. + */ + static inline bool can_log_call() { return false; } + /** Should the return values be logged? + * \return Return true to allow call return values to be logged. + */ + static inline bool can_log_return() { return false; } + + /** Called by interfaces to provide call logging. + */ + static inline void log_call( const tstring &iface, const tstring &funcname, const tstring &log){} + + /// Called by interfaces to provide call return logging. + static inline void log_return( const tstring &iface, const tstring &funcname, const tstring &log, const tstring &retval){} + + + ///\name Exception handling redirection. + //@{ + + /** Should the error be logged? + * \return Return true to allow error to be logged. + */ + static inline bool can_log_exception() { return false; } + + /// Called by exception handlers to provide logging for errors. + static inline void log_exception(const tstring &type, const tstring &desc, const source_info_t &errorSource ,const source_info_t &callSource ){} + //@} + }; + + /** \struct stream_call_logger_t calllog.h comet/calllog.h + * Log calls to a tostream. + * A class/struct that subscribes to and implements the 'call logger' concept for a steam. + * Template paramter is expected to be a class that has a method that creates a tostream + * instance as follows: + * \code + * static tostream *create() + * \endcode + * \param CREATESTREAM Class with a static function \p create() that returns a tostream * + * \sa tofstream_comet call_logger_ + */ + template + struct stream_call_logger_t +#ifdef COMET_DOXYGEN // For documentation + : call_logger_ +#endif + { + static inline bool can_log_call() + { + tostream *ofs = logger(); + return ofs != NULL && ofs->good(); + } + static inline bool can_log_return() { return can_log_call(); } + static inline bool can_log_exception() { return can_log_call(); } + + static inline void log_call( const tstring &iface, const tstring &funcname, const tstring &log) + { + log_( false, iface, funcname, log, tstring()); + } + static inline void log_return( const tstring &iface, const tstring &funcname, const tstring &log, const tstring &retval) + { + log_( true, iface, funcname, log, retval); + } + static inline void log_exception(const tstring &type, const tstring &desc, const source_info_t &errorSource ,const source_info_t &callSource ) + { + COMET_NOTUSED(errorSource); + tostream *ofs= logger(); + if (ofs==NULL) return; // Should never be NULL, as can_log_call() should have been obeyed before calling. + *ofs << _T("Err ") << callSource.source().t_str(); + if ( !desc.empty() ) + { + *ofs << type << _T(": ") << desc ; + } + *ofs << std::endl; + } + + protected: + // Log a function call/return. + static void log_( bool return_val, const tstring &iface, const tstring &funcname, const tstring &log, const tstring &retval) + { + tostream *ofs= logger(); + if (ofs==NULL) return; // Should never be NULL, as can_log_call() should have been obeyed before calling. + + *ofs << (return_val?_T("Out "):_T("In ")) << iface << _T("::") << funcname; + if (!return_val || !log.empty()) + *ofs << _T("(") << log << _T(")"); + + if (return_val && !retval.empty()) + *ofs << _T(" returned ") << retval; + *ofs << std::endl; + + } + // Access to the static logger instance without needing to initialise a + // member. + static tostream *logger() + { + static tostream *ofs_= NULL; + if (ofs_ ==NULL) + { + ofs_ = CREATESTREAM::create(); + if (ofs_ != NULL) + { + // Safely clean up static pointer on module destruct + module().add_object_to_dispose( create_pointer_deleter( ofs_ ) ); + } + } + return ofs_; + } + }; + + namespace impl + { + /** Redirect default logging to calllog. + * \internal + */ + template<> + struct call_logger_redirect_ + { + // This trick allows the user to be able to still override the call logging. + // Without this, log_exception has to call + // call_logger_::log_exception which causes the template to be + // instantiated, and the user is no longer able to specialise + // call_logger_ + template + struct exe + { + static inline void log_exception(const tstring &type, const tstring &desc, const source_info_t &errorSource, const source_info_t &callSource ) + { + call_logger_::log_exception(type,desc,errorSource, callSource); + } + static inline bool can_log_exception() + { + return call_logger_::can_log_exception(); + } + }; + }; + } + +#ifdef COMET_LOGFILE +#ifndef COMET_LOGFILE_DEFAULT +#define COMET_LOGFILE_DEFAULT NULL +#endif +#include + /** \class tofstream_comet calllog.h comet/calllog.h + * Provides a filestream creator as well as the implementation of an output filestream logger. + * Allows overriding of file name. + */ + class tofstream_comet : public tofstream + { + static const char *&filename_() + { + static const char *default_filename= COMET_LOGFILE_DEFAULT; + return default_filename; + } + public: + /// Get the default filename. + static const char *get_default_filename() { return filename_(); } + /// Set the default filename to a new value. + void set_default_filename( const char *filename) { filename_() = filename; } + + tofstream_comet( const char *fname) : tofstream(fname) {} + + /** Create an instance of the logger. + * Returns NULL if the file doesn't exist. + */ + static tostream *create() + { + const char *fname = filename_(); + if (fname == NULL) + return NULL; + return new tofstream_comet(fname); + } + private: + tofstream_comet(const tofstream_comet &); + tofstream_comet &operator=(const tofstream_comet &); + + }; + + /** Override specialisation of the call_logger_. + * \relates call_logger_ + */ + template<> + struct call_logger_ : public stream_call_logger_t + { + }; + +#endif // FILE_LOG + + /** Default interface (com_ptr) logging. + * Specialise to provide custom logging for com_ptr. + \code + template<> + void comet_log_interface(tostream &os, const com_ptr &iface) + { + os << iface->GetName(); + } + \endcode + Specialisations for IUnknown and IDispatch are used by the variant loggers. + * \sa comet_log comet_log_array + * \relates call_logger_ + */ + template< typename IFACE> + void comet_log_interface(tostream &os, const com_ptr &iface) + { + os << _T("0x") << std::hex << reinterpret_cast(iface.get()) << std::dec; + } + + // Forward declarations. + template void comet_log(tostream &os, const T &value); + + namespace impl + { + // Forward declarations. + template inline void default_comet_log(tostream &os, const T &value); + static void comet_log_array_raw(tostream &os, SAFEARRAY *value); + + // The default variant handler. + template< bool B> + static inline void default_comet_log_variant(tostream &os, const variant_t &value, bool out_type) + { + VARTYPE vt = value.get_vt(); + if ((vt & VT_ARRAY) != 0) + { + comet_log_array_raw(os, value.get().parray); + } + else + { + VARIANT varcopy=value.in(); + if (vt == (VT_BYREF | VT_VARIANT)) // Dereference variant by reference + varcopy = *V_VARIANTREF(&varcopy); + const VARIANT *var=&varcopy; + +#define __VARIANT_LOGPOINTER_TYPE_CAST(vartype,cast) \ + case VT_##vartype:\ + if(out_type) os << _T("VT_")_T(#vartype)_T(":");\ + comet_log_interface(os, cast(V_##vartype(var))); \ + break + +#define __VARIANT_LOGPOINTER_REFTYPE_CAST(vartype,cast) \ + case VT_BYREF|VT_##vartype:\ + if(out_type) os << _T("BYREF VT_")_T(#vartype)_T(":");\ + comet_log_interface(os, cast(*V_##vartype##REF(var)));\ + break +#define __VARIANT_LOG_TYPE_CAST(vartype,cast) \ + case VT_##vartype: \ + if(out_type) os << _T("VT_")_T(#vartype)_T(":"); \ + comet_log(os, cast(V_##vartype(var))); \ + break + +#define __VARIANT_LOG_REFTYPE_CAST(vartype,cast)\ + case VT_BYREF|VT_##vartype:\ + if(out_type) os << _T("BYREF VT_")_T(#vartype)_T(":");\ + comet_log(os, cast(*V_##vartype##REF(var)));\ + break + +#define __VARIANT_LOG_TYPE(vartype) \ + case VT_##vartype: \ + if(out_type) os << _T("VT_")_T(#vartype)_T(":");\ + comet_log(os, V_##vartype(var));\ + break + +#define __VARIANT_LOG_REFTYPE(vartype)\ + case VT_BYREF|VT_##vartype: \ + if(out_type) os << _T("BYREF VT_")_T(#vartype)_T(":"); \ + comet_log(os, *V_##vartype##REF(var)); \ + break + + switch (vt) + { + __VARIANT_LOG_TYPE(UI1); + __VARIANT_LOG_TYPE(UI2); + __VARIANT_LOG_TYPE(UINT); + __VARIANT_LOG_TYPE(UI4); + __VARIANT_LOG_TYPE(I1); + __VARIANT_LOG_TYPE(I2); + __VARIANT_LOG_TYPE(INT); + __VARIANT_LOG_TYPE(I4); + __VARIANT_LOG_TYPE(R4); + __VARIANT_LOG_TYPE(R8); + __VARIANT_LOG_REFTYPE(UI1); + __VARIANT_LOG_REFTYPE(UI2); + __VARIANT_LOG_REFTYPE(UINT); + __VARIANT_LOG_REFTYPE(UI4); + __VARIANT_LOG_REFTYPE(I1); + __VARIANT_LOG_REFTYPE(I2); + __VARIANT_LOG_REFTYPE(INT); + __VARIANT_LOG_REFTYPE(I4); + __VARIANT_LOG_REFTYPE(R4); + __VARIANT_LOG_REFTYPE(R8); + + case VT_BOOL: + if(out_type) os << _T("VT_BOOL:"); + os << (V_BOOL(var)==VARIANT_FALSE)?_T("true"):_T("false"); + break; + case VT_BYREF|VT_BOOL: + if(out_type) os << _T("BYREF VT_BOOL:"); + os << (V_BOOL(var)==VARIANT_FALSE)?_T("true"):_T("false"); + break; + __VARIANT_LOG_TYPE_CAST( CY, currency_t::create_const_reference); + __VARIANT_LOG_REFTYPE_CAST( CY, currency_t::create_const_reference); + __VARIANT_LOG_TYPE_CAST( DATE, datetime_t::create_const_reference); + __VARIANT_LOG_REFTYPE_CAST( DATE, datetime_t::create_const_reference); + __VARIANT_LOG_TYPE_CAST( BSTR, bstr_t::create_const_reference); + __VARIANT_LOG_REFTYPE_CAST( BSTR, bstr_t::create_const_reference); + + __VARIANT_LOGPOINTER_TYPE_CAST( UNKNOWN, com_ptr::create_const_reference); + __VARIANT_LOGPOINTER_TYPE_CAST( DISPATCH, com_ptr::create_const_reference); + __VARIANT_LOGPOINTER_REFTYPE_CAST( UNKNOWN, com_ptr::create_const_reference); + __VARIANT_LOGPOINTER_REFTYPE_CAST( DISPATCH, com_ptr::create_const_reference); + + case VT_DECIMAL: + if(out_type) os << _T("BYREF VT_DECIMAL:"); + os << _T("?"); + break; + case VT_ERROR: + if(out_type) os << _T("VT_ERROR:"); + os <<_T("0x") << std::hex << V_ERROR(var) << std::dec; + break; + case VT_BYREF|VT_ERROR: + if(out_type) os << _T("BYREF VT_ERROR:"); + os <<_T("0x") << std::hex << *V_ERRORREF(var) << std::dec; + break; + default: + os << _T("???"); + } +#undef __VARIANT_LOG_TYPE_CAST +#undef __VARIANT_LOG_REFTYPE_CAST +#undef __VARIANT_LOG_TYPE +#undef __VARIANT_LOG_REFTYPE +#undef __VARIANT_LOGPOINTER_TYPE_CAST +#undef __VARIANT_LOGPOINTER_REFTYPE_CAST + } + } + + /* Logging for raw safearrays. + * For vector arrays of size <= 16, this fakes a variant and then uses the default variant logging to log the elements + */ + static void comet_log_array_raw(tostream &os, SAFEARRAY *value) + { + UINT dims = SafeArrayGetDim(value); + VARTYPE vt ; + if( FAILED(SafeArrayGetVartype(value, &vt ) ) ) + { + os << "???"; + return; + } + + long ubound=-1,lbound=0; // Outside so it can be used for the '1' case below. + for (UINT d = 1; d<= dims ; ++d) + { + if( SUCCEEDED(SafeArrayGetUBound( value, d, &ubound) ) && SUCCEEDED(SafeArrayGetLBound(value, d, &lbound) )) + { + if( lbound == 0) + os << _T("[") << ubound+1 << _T("]"); + else + os << _T("(") << lbound << _T("..") << ubound << _T(")"); + } + + } + if (dims == 1 ) + { + long size = 1+ubound-lbound; + if (size ==0) + { + os << _T("{}"); + } + else if( size > 0 && size <= 15) + { + // For small arrays, print out the elements. + os << _T("{"); + VARIANT v; + void *var; + bool first=true, is_variant= (vt==VT_VARIANT); + if ( is_variant) + var = (void *)&v; + else + { + V_VT(&v) = vt; + var = (void *)&V_UI1(&v); + } + + for (long i = lbound; i <= ubound; ++i ) + { + if(first) + first = false; + else + os << _T(","); + if( SUCCEEDED( SafeArrayGetElement( value, &i, var ) )) + default_comet_log_variant( os, variant_t(auto_attach(v)), first | is_variant); + } + os << _T("}"); + } + } + } + + // Default logging: stream the value. + template + inline void default_comet_log(tostream &os, const T &value) + { + // ERRORS: If a compiler error occurs here, you may need to + // specialise comet_log<> to the type T here. + os << value; + } + // Default logging for bstr_t + template<> + inline void default_comet_log(tostream &os, const bstr_t &value) + { + // Put quotes round the string - simplistic, don't worry about + // non-printables or escaping for the moment. + os << _T("'") << value << _T("'"); + } + + // Deafult logging for variants. + template<> + static inline void default_comet_log(tostream &os, const variant_t &value) + { + default_comet_log_variant( os, value, true); + } + } + + /** Type specific logger. + * The default is to use internal logging. This can be specialised to + * provide type-specific logging. + * \sa comet_log_array comet_log_interface + * \relates call_logger_ + */ + template + void comet_log(tostream &os, const T &value) + { + impl::default_comet_log(os,value); + } + + namespace impl + { + // trick to work out whether it is a safearray or com_ptr type (required because of no + // partial specialisation). Needed to handle the special circumstances + template + struct check_log_type_ + { + static long check(...); + + template + static char check(const safearray_t &); + + template + static short check( const com_ptr &); + + static T createT(); + enum { is = sizeof( check( createT() ) ) }; + + }; + template < int TYPE> struct choose_logger; + + // Default safearray_t logger where we know the type T. + // + template + inline void default_comet_log_array(tostream &os, const safearray_t &value) + { + safearray_t::index_type lbound=value.lower_bound(), size = value.size(); + if (lbound == 0) + os << _T("[") << size << _T("]"); + else + os << _T("(") << lbound << _T("..") << (lbound + size -1) << _T(")"); + + if( value.size() <= 15) + { + os << _T("{"); + bool first = true; + for (safearray_t::const_iterator it = value.begin(); it != value.end(); ++it) + { + choose_logger::is >::do_comet_log(os, *it); + if (first) + first=false; + else + os << _T(","); + } + os << _T("}"); + } + } + } + /** Typed safearray_t logger. + * The default is to use internal logging. This can be specialised to + * provide type-specific logging. + * \sa comet_log comet_log_interface + * \relates call_logger_ + */ + template + void comet_log_array(tostream &os, const safearray_t &value) + { + impl::default_comet_log_array(os,value); + } + + namespace impl + { + // choose the standard logger + template < int TYPE> + struct choose_logger + { + template + static inline void do_comet_log(tostream &os, const T &value) + { + comet_log(os,value); + } + }; + // Choose the array logger + template<> + struct choose_logger + { + template + static inline void do_comet_log(tostream &os, const safearray_t &value) + { + comet_log_array(os, value); + } + }; + // Choose the interface logger + template<> + struct choose_logger + { + template + static inline void do_comet_log(tostream &os, const com_ptr &value) + { + comet_log_interface(os, value); + } + }; + + // Choose which of the comet_loggers to use. + template + static inline tostream &do_comet_log(tostream &os, const T &value) + { + choose_logger::is >::do_comet_log(os, value); + return os; + } + } + //@} +} + +#endif /* COMET_CALLLOG_H */ diff --git a/lib/local/CamCom/comet/cmd_line_parser.h b/lib/local/CamCom/comet/cmd_line_parser.h new file mode 100644 index 00000000..65c41789 --- /dev/null +++ b/lib/local/CamCom/comet/cmd_line_parser.h @@ -0,0 +1,101 @@ +/** \file + * Command line parser. + */ +/* + * Copyright 2002 Mikael Lindgren + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_COMMAND_LINE_PARSER_H +#define COMET_COMMAND_LINE_PARSER_H + +#include + +namespace comet { + + /*! \addtogroup Misc + */ + //@{ + class cmd_line_parser + { + public: + enum kind_t { + Name, // Name type token, has no leading / or - + Option, // Option type token. Leading / or - skipped by token + Value, // Value for name or option. Leading : or = skipped by token + Done // No more tokens + }; + + explicit cmd_line_parser(const TCHAR* cmd_line): cmd_line_(cmd_line) + { + reset(); + } + + kind_t get_next_token(tstring& result) + { + static const TCHAR terminators[] = _T("=/- \t"); + static const TCHAR white_space[] = _T(" \t"); + + kind_t kind; + + token_ = next_token_ + _tcsspn(next_token_, white_space); // Skip leading whitespace + switch (*token_) + { + case 0: + return Done; + case _T('-'): + case _T('/'): + kind = Option; + ++token_; + break; + case _T('='): + kind = Value; + ++token_; + break; + default: + kind = Name; + break; + } + if (kind == Option || kind == Value) + token_ += _tcsspn(token_, white_space); // Skip any more whitespace + if (*token_ == _T('"')) + { + const TCHAR* next = _tcschr(token_+1, _T('"')); + if ( next ) + { + result.assign( token_+1, next ); + next_token_ = next+1; + return kind; + } + } + next_token_ = token_ + _tcscspn(token_, terminators); + result.assign(token_, next_token_); + return kind; + } + + void reset() + { + token_ = 0; + next_token_ = cmd_line_; + } + + private: + const TCHAR* cmd_line_; + const TCHAR* token_; + const TCHAR* next_token_; + }; + //@} + +} + +#endif diff --git a/lib/local/CamCom/comet/comet.h b/lib/local/CamCom/comet/comet.h new file mode 100644 index 00000000..b2ad70fc --- /dev/null +++ b/lib/local/CamCom/comet/comet.h @@ -0,0 +1,776 @@ +/** \file + * Load basic comet libraries. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen, Michael Geddes + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_COMET_H +#define COMET_COMET_H + +#include + +/** + @mainpage Comet + + See https://github.com/alamaison/comet + + See \ref comethistory. + + \section cometinfocontents Information Pages +
    +
  • \ref tlb2husage
  • +
  • \ref tlb2handidl
  • +
  • \ref cometcomptr
  • +
  • \ref cometrawcomaccess
  • +
  • \ref cometconnectionpoints
  • +
      +
    • \ref cometconnectionpointssource
    • +
    • \ref cometconnectionpointsink
    • +
    +
  • \ref comettstring
  • +
  • \ref cometdefineconfiguration
  • +
  • \ref cometcatchallexception
  • +
  • \ref cometclassfactory
  • +
  • \link custom_registration Custom Registration\endlink
  • +
  • \ref comethandleexception
  • +
  • \ref cometcalllogging
  • +
  • \ref cometautoexp
  • +
    • \ref cometautoexpusage
    +
+ */ + +/** + * \page cometrawcomaccess Raw COM Acces + * The designers of Comet have tried to avoid making the wrappers do too much + * implicitly, prefering discrete but explicit ways of triggering functionality. + * + * In order to provide interaction of Comet classes with their COM counterparts, + * a standard set of methods have been used, effectively providing a COM wrapper + * concept. + * + *
    + *
  • in(): Return a pointer suitable for [in] parameters,
  • + *
  • out(): Return a pointer suitable for [out] parameters, first freeing memory allocated,
  • + *
  • inout(): Return a pointer suitable for [in,out] parameters.
  • + *
  • detach(): Detach and return the internal pointer from the object
  • + *
  • in_ptr(): {optional} Return a pointer suitable for [out] parameter where a pointer is required, first freeing memory allocated
  • + *
  • get(): {optional} Returns a pointer to the internal pointer (same as STL pointers)
  • + *
  • auto_attach( RAW_TYPE ): Cause an assignment to attach the object to the specified raw COM type.
  • + *
  • create_reference( RAW_TYPE ): Static method to create a propper wrapper reference to the specified raw object.
  • + *
  • create_const_reference( RAW_TYPE ): Static method to create a const wrapper reference to the specified raw object.
  • + *
+ */ + +/** \page cometcatchallexception Exception Catching Policy +In an ideal world, Microsoft would have adhered to the C++ standard, and made +catch(...) not catch system exceptions like 'Access Violation', +however this is not the case. + +To make it worse, not only do system exceptions get caught by catch(...), +they also bypass the stack unwinding. This is quite problematic in an +environment where this mechanism is relied upon for resource management, commonly +'only' memory, but also including multithreading acces control mechanisms. + +One of the big problems with Access Violations is that it is an unplanned +exception, and in many situations will leave data structures incomplete, +resources locked or other equally vicious artifacts. Essentially, it breaks +invariants. One viable take on this is that if you don't know where an +exception came from, you shouldn't be catching it. + +A big advantage of letting access violations and it's ilk (or any other +unplanned exception) through is that these will then get caught by the JIT (Just +In Time) handlers such as Dr. Watson and Compiler environments that allow +immediate or post-mortem debugging of the problem. A lot of projects (like +Mozilla) have their own bug-reporting which can intercept the bugs at the top +level and generate their own crash dumps, and catching these exceptions is +actually going to bypass a very useful mechanism. + +All this is to say that we have made a shift away from exception handling +policies that cause system expeptions to be caught, however in order to +allow as much flexibility as possible, an improved exception handling mechanism +has been added to the interface implementations and to the server implementation +that uses catch and rethrow to allow logging and custom handling of all exceptions. + +The default action of the mechanisms will be to finally rethrow unknown +exceptions. The good thing about this is that the rethrow behaves to a JIT handler +as if the expeption had never been caught. + +*/ + +/** \page cometautoexp Comet AutoExp.Dat Support +There is a largely undocumented feature is Visual Studio 6 and 7 that allows +you to create a dll 'ADDIN' that provides custom formatting for variables in the +debugger. CometAutoExp.dll allows you to view simple automation types in more +detail than currently provided internally by the debugger. + +By enabling this, you will get to see real date/times when you view datetime_t, you +get to see SAFEARRAY types (including IIDs), and possibly their contents, you +will see full VARIANT types, including SAFEARRAYs. + +The file in comet/src/cometautoexp/AutoExp.dat shows the entries that will +be needed to included in your own AutoExp.dat for this to work. (Usually +found at "c:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin\AUTOEXP.DAT") + +See \ref cometautoexpusage for more information on how to use this. + */ + +/** \page cometdefineconfiguration Comet Configuation + While Comet barely uses macros for the import work of creating a COM library, + there are a number of preprocessor switches that are used to alter certain + behaviours and select code. While most of these refer to the compiler being used, + there are a few that are intended to be used as external switches. + +
  • COMET_ALLOW_DECLSPEC_PROPERTY : Wrapper headers include declspec + properties, allowing interface properties to be assigned-to and read as + properties.
  • COMET_ASSERT_THROWS : Comet throws + comet::assert_failed when internal assertions fail in debug builds.
  • +
  • COMET_ASSERT_THROWS_ALWAYS : Comet throws comet::assert_failed when + internal assertions fail in both debug and release builds.
  • +
  • COMET_LOGFILE : See \ref cometcalllogging.
  • +
  • COMET_LOGFILE_DEFAULT : See \ref cometcalllogging.
  • +
  • COMET_NO_MBCS : Disable comet multi-byte-character-set conversion handling.
  • +
  • COMET_NO_MBCS_WARNING : Turn off the warning indicating that sdt::string is not compatible with MBCS.
  • +
  • COMET_USE_RAW_WRAPPERS : Tells com_ptr not to use the comet wrappers but to return raw interface pointers.
  • +
+ + There are also a number of macros that are either utitlity macros, reflect compiler differences + or Comet version differences that may be convenient to use within your own code. + +
    +
  • COMET_ASSERT(expression) : Run-time assert.
  • +
  • COMET_STATIC_ASSERT(static-expression) : Compile-time assert.
  • +
  • COMET_NOTUSED(info) : Specify unused argument.
  • +
  • COMET_FORCEINLINE : Use before a method to apply force-inline if available.
  • +
  • COMET_VARIANT_TRUE,COMET_VARIANT_FALSE : Use in place of VARIANT_TRUE,VARIANT_FALSE to avoid type warnings.
  • +
  • COMET_MAJOR_VER : Major comet version
  • +
  • COMET_MINOR_VER : Minor comet version
  • +
  • COMET_BUILD : Comet version to compare against (yyyymmdd of distribution)
  • +
  • COMET_STRICT_TYPENAME : Used for when stricter compilers require 'typename' keyword and VC6 has an error.
  • +
  • COMET_CONST_TYPE(vartype, varname, value) : Define a typed constant for a class/struct (or use enum if not supported)
  • +
+ + */ + +/** \page comethistory Comet History +\par Version 1 gamma 32 ( ) +\li Support for VC++2005 (Sofus Mortensen) +\li Numerous bug fixes and better support for Intel C++ (Richard Crossley) +\li Fixed off-by-one bug regarding array sizes (Yunusov Bulat) + +\par Version 1 beta 31 ( ) +\li safearray.h - Fix a few omissions/bugs (Lijun Qin) +\li comet_lint.lnt - Add pclint options file. (Michael Geddes) +\li server.h - Fix singlton_autorelease class (Ralf Jrgensen) +\li datetime.h - Fix bug in century non-leap-years. (Michael Geddes) +\li datetime.h - Use julian date conversions from boost - to drastically improve split/join efficiency (Michael Geddes) +\li datetime.h - Make invalid() rather than valid() the default (Michael Geddes) +\li date.h - Make use of above improvements in datetime_t (Michael Geddes) +\li datetime.h/date.h - Add ability to construct/assign invalid/null dates from dt_null, dt_invalid (Michael Geddes) +\li datetime.h - Fixed bug in to_unixtime (Lijun Qin) +\li array.h - Fixed typename problem (Sofus Mortensen) +\li safearray.h - Fixed problem with sa_debug_iterator and MSVC 7.1 (Sofus Mortensen) +\li uuid_fwd.h - Fixed bug in !operator (Sofus Mortensen) + +\par Version 1 beta 30 (2004-3-5) +\li Support for VC7.1 partial specialisation. (Lijun Qin) +\li datetime.h - Bugfix in datetime_t conversion (Gordon Donaldson) +\li safearray.h - Separate safearray_t::resize into resize and resize_bound to avoid ambiguous conversions. (Michael Geddes) +\li safearray.h - Fix bug in safearray_t:resize_bound using an incorrect copy. (Michael Geddes) +\li safearray.h - Add safearray_t debug iterator for debug builds. (Michael Geddes) +\li safearray.h - Add more comments for safearray_t (Michael Geddes) +\li safearray.h - Support more container-type methods (erase,push_front,push_back,pop_front,pop_back) (Michael Geddes) +\li server.h - Add auto-release singleton (Michael Geddes) +\li uuid.h - uuid_t::operator! no longer reversed (Lijun Qin) +\li uuid.h - fix ambiguous ++ operator usage. (Lijun Qin, Bulat Raditch, Michael Geddes) +\li handle.h - Extension of handles for GDI objects for cometw (Michael Geddes/Vladimir Voinkov/Lindgren Mikael) +\li bstr.h - Throw comparison errors if they occur. (Michael Geddes) +\li cp.h - add 'is_connected' method to connection points. (Lijun Qin) +\li common.h - Support for pclint (Michael Geddes, Gabriel Barta) +\li datetime.h - Support for timezones (Michael Geddes) +\li datetime.h - Fix up buffer handling (Michael Geddes, Lijun Qin) +\li variant.h - Support for strings with embedded nulls (Michael Geddes) +\li tlb2h - Support for bool in structs and in safearrays (Michael Geddes) +\li tlb2h - Support for wrapper renaming of specific methods on specific interfaces/struct/unions. (Michael Geddes) +\li tlb2h - Dual interfaces inherited from an interface in a different type-library now works when called by dispatch (Michael Geddes) +\li tlb2h - Output of HTML now better restricted to only required interfaces. (Michael Geddes) +\li tlb2h - Obey COM rules that require [out] pointers to be initialised on failure (Michael Geddes) +\li tlb2h - Support implementation of an interface that inherits from an interface from a different typelibrary (Michael Geddes) +\li cometautoexp - Support for more types, and better handling of strings. (std::basic_string, COLEDateTime) (Michael Geddes) +\li date.h - Added dateonly_t class. (Michael Geddes) +\li uuid.h - Make the strings accessors consistant (Michael Geddes, Lijun Qin) + +\par Version 1 beta 29 (2003-12-30) +\li Added loads of documentation, reduced doxygen warnings to 1(Michael Geddes) +\li Added CometAutoExp.dll extended support for AutoExp.dat +\li Remove unnecessary catch(...) - use specific catches (Michael Geddes) +\li Support create_const_reference in all wrapper classes. (Michael Geddes) +\li tlb2h - Enhance support for error information (interface/coclass name) (Michael Geddes) +\li tlb2h - Add support for logging calls & exceptions. (Michael Geddes) +\li tlb2h - Support for special characters in help strings. (Michael Geddes) +\li tlb2h - Few tweaks to get correct output. (Michael Geddes) +\li tlb2h - Option to show version (Michael Geddes) +\li bstr.h - Work with _STLP_DEBUG wrappers (Michael Geddes) +\li bstr.h - Constructor works with MBCS (Michael Geddes) +\li bstr.h - Support for {} round uuid_t (Michael Geddes) +\li cp.h - Add convenience methods. (Michael Geddes) +\li datetime.h - Constructor to supply date and time (Michael Geddes) +\li datetime.h - Support output streams. (Michael Geddes) +\li error.h - Support for source, iid and helpfile in errors. (Michael Geddes) +\li interface.h - Provide extra comtype definitions. (Michael Geddes) +\li interface.h - Provide convenience macro to define extra comtype with IID_ instances. (Michael Geddes) +\li ptr.h - Add identity_ptr class that references an identity IUnknown. All other comparisons QI on both pointers. (Michael Geddes) +\li server.h - Add support for singelton classes. (Michael Geddes) +\li tlbinfo.h - Fix up reference counting on struct wrappers. (Michael Geddes) +\li tlbinfo.h - Support ITypeInfo2 (Michael Geddes) +\li variant.h - Added explicit converts (as_uchar(), as_uint(), as_double() etc) (Michael Geddes) +\li variant.h - Added is_empty(), is_null() and is_nothing() (Michael Geddes) + +\par Version 1 beta 28 (2003-6-18) +\li Support for Visual Studio 2003 + +\par Version 1 beta 27 (2003-3-5) +\li tlb2h - bug fix variant_t members of dispinterfaces (Michael Geddes) +\li tlb2h - support for safearrays of interfaces (Michael Geddes) + +\par Version 1 beta 26 (2003-2-1) +\li tlb2h - major update. See http://groups.yahoo.com/group/tlb2h/message/706 (Michael Geddes) +\li Enum.h - allow copy policies with members (Michael Geddes) +\li datetime.h - fixed bug in timeperiod_t::split (Michael Geddes) + +\par Version 1 beta 25 (2002-11-28) +\li Connection point implementation is now customisable via traits. See http://groups.yahoo.com/group/tlb2h/message/688 (Michael Geddes) +\li Fixed bug in str_formattime in datetime.h (Sofus Mortensen). +\li Fixed bug in add_months in datetime.h (Michael Geddes). + +\par Version 1 beta 24 (2002-11-13) +\li Major update of tlb2h. See http://groups.yahoo.com/group/tlb2h/message/659 and http://groups.yahoo.com/group/tlb2h/message/661. (Michael Geddes) +\li Fixed problem with variant_t::operator==. (Kyle Alons) +\li Fixed bug in bstr_t::s_str(), bstr_t::is_empty(), bstr_t::length() (Mikael Lindgren, Michael Geddes) +\li safearray_t: 1. Changed the constructor that takes two iterators to not have a default lowerbound (to be consistant with the other constructor) +2. Changed resize to allow specifying a lower-bound. +3. Changed the default lb to be 0 in the case where none is specified and where the safearray is NULL. (Michael Geddes) + +\par Version 1 beta 23 (2002-9-1) +\li Bug fixes to namespace handling. (Michael Geddes) +\li Added friendly error message for missing tlbinf32.dll. (Sofus Mortensen) +\li Worked around MSVC6 internal compiler error when contructing an enum. (Michael Geddes) +\li Bug fixes to currency.h (Michael Geddes) +\li Bug fixes to datetime.h (Michael Geddes) + +\par Version 1 beta 22 (2002-8-19) +\li Update of tlb2h - better handling of namespace mangling, and support for +unwrapping the type of aliases without unwrapping the name of the aliases (Michael Geddes). +\li Fixed bug in uuid_fwd. (John Harrison) +\li Added oleidl_comtypes.h. (Gabriel Barta) +\li Restored old functionality of try_cast - will no longer throw when casting a null pointer. (Sofus Mortensen) +\li New auto-locking implementation of safearray_t::create_reference and safearray_t::create_const_reference. (Michael Geddes, Sofus Mortensen) +\li Included first drop (pre-alpha version) of cometw. (Vladimir Voinkov) + +\par Version 1 beta 21 (2002-6-21) +\li Fixed null pointer bug in com_error::what(). (Kyle Alons) +\li Fixed bug to do with the unloading of typelibraries when refcount reaches zero. (Michael Geddes) +\li Added support for MBCS to std::string and const char* conversions for bstr_t. Only active when _MBCS is defined. (Michael Geddes) +\li Fixed locking/unlocking bug. (Vladimir Voinkov, Sofus Mortensen) +\li Fixed bug in safearray_t::create_reference(variant_t&). (Michael Geddes) +\li Various fixes to datetime and currency. (Michael Geddes) +\li Added constants COMET_VARIANT_TRUE and COMET_VARIANT_FALSE in order to avoid warnings. (Michael Geddes, Sofus Mortensen) +\li registry::value has been extended with overload for method str and dword, where a default value can be supplied if the key/value does not exist. (Sofus Mortensen) +\li Various patches for BCC. (Michael Geddes) +\li Solved problem with vtables gaps when inheriting from those interfaces. (Michael Geddes) +\li Removed a few level 4 warnings. (Michael Geddes) +\li Added experimental HTML generation to tlb2h. (Sofus Mortensen) + +\par Version 1 beta 20 (2002-4-9) +\li tlb2h will fall back to using raw structs for structs with types that cannot be wrapped. (Sofus Mortensen) +\li Added application wizard for MSVC.NET. See tlb2h/src/Comet. (Sofus Mortensen) +\li Eliminated a few warnings. (Sofus Mortensen) + +\par Version 1 beta 19 (2002-3-26) +\li Added caching of IRecordInfo to sa_traits for wrapped structs and ITypeInfo to IDispatch implementations. (Sofus Mortensen) +\li Fixed problem with safearray_t iterators on MSVC7. (Sofus Mortensen) +\li Fixed bug regarding range checking for safearray_t::at being off by one. (Sofus Mortensen) +\li Added range checking assertion (debug only) to safearray_t::operator[]. (Sofus Mortensen) +\li Changed safearray_t constructor, so that lower bound of one is no longer assumed. Instead lower bound must be specified. (Sofus Mortensen) + +\par Version 1 beta 18 (2002-3-25) +\li Major revision of bstr_t. (Sofus Mortensen) +\li Added experimental support for wrapped structs to tlb2h and safearray. (Sofus Mortensen) +\li Fixed problem with having a coclass as a connection point sink. (Sofus Mortensen) +\li Revised treatment of [in, out] parameters in tlb2h. The old implementation could cause double deletion of [in. out] parameters when an exception was thrown. (Sofus Mortensen) +\li Revised all wrapper classes, in order for create_reference to create a mutable reference and create_const_reference an immutable reference. (Sofus Mortensen) +\li Revised locking in safearray_t. safearray_t::create_reference and safearray_t::create_const_reference is no longer doing automatic locking. (Sofus Mortensen) +\li tlb2h now generates #import alike smart pointer typedef. Ie. interface IFoo will be matched with a typedef com_ptr IFooPtr. (Sofus Mortensen) +\li Added support for datetime_t and currency_t to safearray_t. (Sofus Mortensen) +\li Added t_str() to bstr_t for converting to tstring. (Sofus Mortensen) + +\par Version 1 beta 17 (2002-2-18) +\li Fixed bug in atl_module. (Michael Geddes) +\li Documentation update for bstr.h. (Michael Geddes) +\li bstr_t now supports strings with embedded nulls. Thanks to Eric Friedman for reporting this. (Sofus Mortensen) +\li Removed use of _alloca in bstr.h. (Sofus Mortensen) + +\par Version 1 beta 16 (2002-2-12) +\li Fixed bug in functors bstr::less, bstr::less_equal, bstr::greater, bstr::greater_equal, bstr::equal_to and bstr::not_equal_to. (Michael Geddes) +\li tlb2h: switched ResolveAliases off per request from Michael Geddes. +\li tlb2h: Removed dummy wrapper methods (see http://groups.yahoo.com/group/tlb2h/message/476). (Sofus Mortensen) + +\par Version 1 beta 15 (2002-2-1) +\li Added lw_lock class based on Brad Wilson's LightweightLock (see http://www.quality.nu/dotnetguy/archive/fog0000000007.aspx). Many thanks to Brad for making LightweightLock open source. (Sofus Mortensen) +\li Comet now works with MSVC7. (Sofus Mortensen) +\li Removed functor.h. (Sofus Mortensen) +\li Fixed serious bug in bstr_base - causing memory corruption when concat'ing bstr's. (Michael Geddes) +\li Fixed obvious typo in locking_ptr. (Vladimir Voinkov) +\li Removed unnecessary include dependency from scope_guard.h. (Vladimir Voinkov) +\li Fixed compatibility problem with STLport for bstr comparison functors. (Michael Geddes) +\li Removed level 4 warnings from currency.h. (Michael Geddes). +\li Fixed problem with the -c option in tlb2h. (Michael Geddes). +\li Fixed bug in ATL_module.h. (Michael Geddes) +\li impl_dispatch is now lazy loading TypeInfo. Better performance for dual interfaces where client is not using IDispatch. (Sofus Mortensen) +\li Fixed various bugs in tlb2h. (Sofus Mortensen) + +\par Version 1 beta 14 (2002-1-17) + +\li Fixed problem with dispinterfaces and [in, out] enum parameters. (Sofus Mortensen) +\li Added simple system to prevent compiling out-of-date tlb2h generated headers. (Sofus Mortensen) +\li Fixed bug in impl_dispatch. (Michael Geddes, Sofus Mortensen) + +\par Version 1 beta 13 take 4 (2002-1-11) + +\li Fixed problem with dispinterfaces and enum parameters reported by Kyle Alons. (Sofus Mortensen) +\li Fixed serious bug in iterators for safearray_t reported by Steve Broeffle. The bug was caused by a MSVC compiler bug. (Sofus Mortensen) +\li Fixed bug operator in operator[] in iterators for safearray_t. (Sofus Mortensen) + +\par Version 1 beta 13 take 3 (2002-1-9) + +\li Fixed bug in uuid_t introduced in beta 13 take 2. (Sofus Mortensen) + +\par Version 1 beta 13 take 2 (2002-1-7) + +\li Beta 13 couldn't compile with _UNICODE defined. Fixed now. (Sofus Mortensen) + +\par Version 1 beta 13 (2002-1-7) + +\li Added basic support for writing exe servers in Comet. (Mikael Lindgren, Sofus Mortensen) +\li Added simple command line parser class to new header comet/cmd_line_parser.h (Mikael Lindgren) +\li Added new header comet/tstring.h. Defines tstring, tistream, tostream, etc, etc. (Sofus Mortensen) +\li Fixed various bugs in uuid_t. (Sofus Mortensen) + +\par Version 1 beta 12 (2001-12-20) + +\li Revision of embedded_object. embedded_object now takes parent type as first argument, arguments 2, 3, 4, .. denotes interfaces to implement. + embedded_object_1 has been removed. embedded_object2 has been added, extends embedded_object with functionality to + disconnect a child object from its parent. (Sofus Mortensen with help from Kyle Alons). +\li Added new file dispatch.h with specialisation of wrap_t with similar (but superior) functionality as CComDispatchDriver. +\li Added new class dynamic_dispatch to dispatch.h. Use dynamic_dispatch to dynamically implement IDispatch - adding methods and properties at runtime. (Sofus Mortensen) +\li Changed interface of sink_impl so that unadvise no longer takes an argument. Additionally the destructor will unadvise if necessary. (Sofus Mortensen) +\li Added new file uuid.h with GUID/UUID/CLSID wrapper called uuid_t. (Sofus Mortensen) +\li Changed tlb2h to support new wrapper uuid_t. (Sofus Mortensen) +\li Fixed potential thread safety problem regarding class factories. (Sofus Mortensen, Paul Hollingsworth) + +\par Version 1 beta 11 (2001-11-16) + +\li Major fix to tlb2h. tlb2h now supports properties in dispinterfaces. (Sofus Mortensen) +\li Module constants are now static. (Sofus Mortensen) +\li tlb2h now skips non-COM interfaces that do not derive (directly or transitively) from IUnknown. (Sofus Mortensen) +\li Fixed problem with error messages ending up as garbage in tlb2h. (Kyle Alons) +\li Various fixes regarding safearray_t. (Michael Geddes and Sofus Mortensen) +\li Various other minor fixes. (Sofus Mortensen) + +\par Version 1 beta 10 (2001-10-17) + +\li class coclass changed so that the 3, 4, ... template parameters specifify additional interfaces to implement. + Example: + \code + template<> coclass_implementation : public coclass + \endcode + (Sofus Mortensen) +\li Aggregating another component is now done by adding class aggregates to the type list of interfaces. First template argument of class aggregates is the coclass + to aggregating. The 2, 4, ... template arguments specify which interfaces to aggregate. If no interfaces have been specified, all interfaces will be aggregated. + Example: + \code + template<> coclass_implementation : public coclass > + \endcode + (Sofus Mortensen) +\li Added cmp method to bstr_base which a.o.t. can be configured for case insensitive comparision. (Michael Geddes) +\li Added comparison functors based on cmp to bstr_base, less, less_equal, equal_to, etc. Example of usage: + \code + std::set > mySet; + \endcode + (Sofus Mortensen) +\li Fixed bugs in assignment operators for datetime_t. (Sofus Mortensen, Mikael Lindgren) +\li In ptr.h changed COMET_ALLOW_DECLSPEC_PROPERTY to COMET_USE_RAW_WRAPPERS. (Michael Geddes) +\li In common.h added workaround for VARIANT_TRUE resulting in a level 4 warning. (Michael Geddes) +\li Changed server.h, so that a compiler error will occur if there are unimplemented coclasses. In case you deliberately want + unimplemented coclass define COMET_ALLOW_UNIMPLEMENTED_COCLASSES in std.h. (Sofus Mortensen) +\li Added various helper functions to util.h. (Sofus Mortensen) +\li Added support for aggregating the free threaded marshaler. Example: + \code + template<> coclass_implementation : public coclass + \endcode + (Sofus Mortensen) +\li Various bug fixes and changes. (Sofus Mortensen) + +\par Version 1 beta 9 (2001-9-23) + +\li Now wrapper properties and methods are only available through com_ptr::operator->. (Sofus Mortensen) +\li Added ostream<> outputting facility to both variant_t and bstr_t. (Sofus Mortensen) +\li Added std::string conversions to variant_t. (Sofus Mortensen) +\li Fixed various bugs in tlb2h regarding dispinterfaces. (Sofus Mortensen). +\li Fixed bug in com_ptr - try_cast'ing from variant_t to com_ptr did not throw an exception on error. (Sofus Mortensen) +\li Made com_ptr constructor from variant_t and assignment from variant_t private in com_ptr to prevent misuse. Use com_cast or try_cast. (Sofus Mortensen) + +\par Version 1 beta 8 (2001-9-19) + +\li Fixed bugs in tlb2h regarding datetime_t. (Sofus Mortensen) +\li Wrapper properties are now only available when accessing an interface through com_ptr::operator->. (Sofus Mortensen) +\li Classes currency_t and datetime_t are now exception safe. (Sofus Mortensen) +\li Added conversions for variant_t from/to currency_t and datetime_t. (Sofus Mortensen) +\li Added conversions for std::wstring to variant_t, solving various ambiguity problems. (Sofus Mortensen) +\li Re-arranged various header files, solving include problems. (Sofus Mortensen) + +\par Version 1 beta 7 (2001-9-7) + +\li Added support for locking to safearray_t. (Michael Geddes) +\li Improved support in tlb2h for methods taking arrays as argument. (Sofus Mortensen) +\li Fixed bug in tlb2h regarding methods that were the parameters were wrongly given a default value. (Sofus Mortensen) +\li Fixed bug in tlb2h regarding type "void *". (Sofus Mortensen) +\li Fixed various bugs in datetime.h. (Michael Geddes) +\li Added COMET_ASSERT - a replacement for _ASSERTE. (Sofus Mortensen) + +\par Version 1 beta 6 take 2 (2001-8-20) + +\li Fixed dumb, dumb _ASSERTE bug in tlb2h (Sofus Mortensen). + +\par Version 1 beta 6 (2001-8-19) + +\li Changed distribution package - now including scripts for generating docs and the source for both tlb2h and the Comet App. wizard (Sofus Mortensen). +\li Wrapped string constant in server.h with _T( ) for UNICODE compatibility (Sofus Mortensen). +\li Tlb2h.exe now generates vtable fillers to cater for interfaces with gaps in the vtable. Such are typically produced by VB (Michael Geddes and Sofus Mortensen). +\li Bug fixes to date_t (Michael Geddes). +\li Elimination of compiler warnings in safearray_t and registry.h (Michael Geddes). + +\par Version 1 beta 5 (2001-8-14) + +\li tlb2h.exe now generates corrects defaults for [optional] VARIANT. variant_t has been updated in order to support this (Sofus Mortensen). +\li Fixed problem in tlb2h.exe with using COMET_ALLOW_DECLSPEC_PROPERTY when importing msado20.tlb (Sofus Mortensen). +\li Fixed problem in tlb2h.exe with aliases being treated as records (Michael Geddes). +\li Several bug fixes to nutshell generation (Michael Geddes). +\li Changed tlb2h, so that the generated headers only #include the headers needed (Michael Geddes). + +\par Version 1 beta 4 (2001-7-25) + +\li datetime_t has been updated and support for datetime_t has been added to tlb2h.exe (Michael Geddes). +\li typelist::index_of bug fix (Michael Geddes, Sofus Mortensen, thanks to Eric Friedman for reporting this). +\li typelist::type_at bug fix (Michael Geddes). +\li create_reference added to safearray_t (Michael Geddes). +\li Experimental attach_from / attach_to added to safearray_t (Michael Geddes). +\li Bug fix in variant.h (Sofus Mortensen). + +\par Version 1 beta 3 (2001-7-14) + +\li MSVC7 beta 2 compatibility fixes (Sofus Mortensen). +\li Various bug fixes in tlb2h (Sofus Mortensen). +\li Added wizard option to tlb2h for generating skeleton implementation of coclasses (Michael Geddes). +\li Various bug fixes to safearray.h (Michael Geddes). +\li variant_t now supports converion to/from saferray_t (Michael Geddes, Sofus Mortensen) +\li com_ptr can now be used to wrap a pointer to coclass_implementation (Michael Geddes). +\li enum.h and cp.h has been updated to support the changes to com_ptr (Michael Geddes). +\li Preliminary version of datetime_t has been added (Michael Geddes). + +\par Version 1 beta 2 (2001-7-04) + +\li Major documentation update (Michael Geddes). +\li Bug fix in tlb2h.cpp (Michael Geddes). +\li Replaced operator bool in com_ptr with a more bullet proof alternative (Sofus Mortensen). +\li Updated functor.h to make use of partial specialisation for compilers that support it (Michael Geddes). + +\par Version 1 beta 1 (2001-6-29) + +\li Added currency support to variant_t (Michael Geddes). +\li Documentation update for server.h (Michael Geddes). +\li Various bug fixes to do with agg-objects (Michael Geddes). +\li Added GetClassObject implementation to atl_module (Michael Geddes). + + +\par Version 1 alpha 10 (2001-6-26): + +\li CURRENCY wrapper updated by Michael Geddes. +\li tlb2h has been updated by Michael Geddes (again!). This time with options to specify which namespace to put wrappers in, and an options to emit symbols only for faster compilation. +\li atl_module.h documentation updated by Michael Geddes. + +\par Version 1 alpha 9 (2001-6-21): + +\li CURRENCY wrapper added by Michael Geddes. +\li Nutshell wrappers didn't work in alpha 8. Now fixed. +\li tlb2h has been updated by Michael Geddes for better command line handling. It now supports multiple files including wildcards, and support for specifying an output directory. +\li Server implementation updated by Michael Geddes to support loading type libraries that has not yet been registered. +\li try_cast no longer throws on null pointers, and comet::com_ptr::operator->() now throws on null pointers. (Michael Geddes) + +\par Version 1 alpha 8 (2001-6-10): + +\li Compatibility patches for GCC/MINGW submitted by Michael Geddes. +\li Updated com_error with methods for accessing wrapped HRESULT value and IErrorInfo (source, helpfile, etc). +\li Various minor bug fixes. + +\par Version 1 alpha 7 take 3 (2001-3-31): + +\li Added converters for CURRENCY to variant_t.. +\li Updated functor.h. + +\par Version 1 alpha 7 take 2 (2001-3-28): + +\li Fixed bug concerning using com_cast and try_cast with raw interface pointers. +\li Fixed bug in bstr.h +\li Fixed dependency on comet/stl.h in comet/enum.h + + +\par Version 1 alpha 7 take 1 (2001-3-26): + +\li Support for dispinterfaces has been added by Michael Geddes and Mikael Lindgren. +\li BCC compatibility patches contributed by Michael Geddes. +\li Support for multiple connection points added by Michael Geddes. +\li Added generalised functor library that works with MSVC (Not really COM related) +\li comet::make_list\<\> has changed behaviour. Instead of writing make_list\, you have to write \link comet::make_list make_list\::result \endlink. This has been done in order to shorten error messages relating typelists. +\li Added several helper classes to comet/typelist.h for manipulation of typelists. +\li Fixed various "ambiguous conversion" errors + + +\par Version 1 alpha 6 (2001-3-7): + +\li Fixed disastrous bug in comet::com_ptr. +\li Re-organisation of header files. +\li Support for Intel C++ 5.0 added. +\li Fixed bug in regkey. +\li Various bugs fixes. + + +\par Version 1 alpha 5 take 2 (2001-1-26): + +\li Fixed bug in ptr.h + + +\par Version 1 alpha 5 (2001-1-16): + +\li Support for implementing aggregateable components. (Michael Geddes) +\li comet::com_ptr is no longer using common base class. +\li Various bug fixes. + + +\par Version 1 alpha 4.2 (2000-12-17): + +\li Fixed bug in critical_section. + + +\par Version 1 alpha 4.1 (2000-12-14): + +\li Fixed bug in variant.h +\li Added support for DATE and CURRENCY to tlb2h. (We might need wrapper classes for those). +\li Fixed missing inline in common.h. + + +\par Version 1 alpha 4 (2000-12-12): + +\li Generated wrappers, interfaces, etc are now placed in namespace corresponding to typelibrary name by tlb2h. +\li Fixed bugs in variant.h +\li The generated wrappers are no longer using .get_raw() and .get_raw_ptr(). Instead the wrapper methods .in(), .in_ptr(), .inout() and .out() are being used. +\li Support for constants placed in type library modules. (See Joav Kohn's posting on ATL discuss). +\li Support for version numbering for ProgID's. (Thanks to Mikael Lindgren). +\li Updated the wizard. + + +\par Version 1 alpha 3 (2000-11-29): + +\li Added sanity checking to safearray_t to capture type mismatches. +\li Added in() method to all wrapper classes for use when calling raw interfaces. +\li The generated header files are now using a normal include guard instead of #pragma once. +\li Various minor fixes. + + +\par Version 1 alpha 2 (2000-11-20): + +\li Nutshell generation added to tlb2h.exe. +\li Added STL compatible SAFEARRAY wrapper called comet::safearray_t. +\li Updated registry class. +\li New BSTR wrapper, comet::bstr with optional reference counting. +\li comet::bstr_t is now a typedef of comet::bstr. +\li ATL support added. + + +\par Version 1 alpha 1 (2000-10-29): + +\li First alpha release of Comet. + + +\par Version 0.9.7 (2000-10-19): + +\li typedef bug fix in tlb2h.exe +\li Bug fixes in comet::variant_t + + +\par Version 0.9.6 (2000-10-19): + +\li Documentation updates. +\li Misc. bug fixes. + + +\par Version 0.9.5 (2000-10-5): + +\li Documentation updates. +\li Paul Hollingsworth has been busy minimising include dependencies in the header files. +\li Added [out] and [in, out] adapters for dealing with raw interfaces. + + +\par Version 0.9.4 (2000-10-4): + +\li Fixed more bugs in comet::com_ptr. +\li Added documentation on comet::com_ptr. + + +\par Version 0.9.3 (2000-9-26): + +\li Fixed various bugs in comet::com_ptr. + + +\par Version 0.9.2 (2000-9-25): + +\li Updated tlb2h to use const where appropiate. +\li Added support for unions. +\li Added support for one-dimensional arrays in structs. + + +\par Version 0.9.1 (2000-9-21): + +\li Experimental support in tlb2h.exe for optional method arguments (ie. attribue optional or defaultvalue). + + +\par Version 0.9.0 (2000-9-19): + +\li Paul Hollingsworth has contributed automatic implementation of IProvideClassInfo. +\li Better support for [in, out] parameters. +\li Fixed bugs in tlb2h's generation of connection point wrapper. +\li Removed progid from template parameters, instead override function get_progid(). +\li Added version metadata to coclass and typelibrary wrappers. +\li Fixed bug in variant_t::variant_t(const comet::bstr_t&). + + +\par Version 0.8.3 (2000-9-12): + +\li Removed DLL dependency for tlb2h.exe. + + +\par Version 0.8.2 (2000-9-7): + +\li com_error / raise_exception_t has been revised. +\li Fixed bug in com_ptr::operator=(int) +\li Rearranged header inclusion introducing error_fwd.h. +\li Added misc. utility classes not necessarily related to tlb2h to tlb2h/utility.h. +\li Updated tlb2h.exe with more types. + + +\par Version 0.8.1 (2000-9-3): + +\li Type conversions to/from bstr_t has been changed/tweaked. +\li bstr_t now supports concatenation through operator+ and operator+=. + + +\par Version 0.8.0 (2000-8-31): + +\li Fixed several bugs in tlb2h.exe. +\li Added misc. utility classes not necessarily related to tlb2h to tlb2h/utility.h. + + +\par Version 0.7.10 (2000-8-25): + +\li Updated AppWizard to generate uuid for type library. +\li Added trait class to com_server for servers with no embedded type library. + + +\par Version 0.7.9 (2000-8-21): + +\li bstr_t is now capable of converting to std::string as well. +\li Rearranged util.h into list.h, common.h and util.h. + + +\par Version 0.7.8 (2000-8-18): + +\li Added simple AppWizard for creating DLL projects. +\li Removed untie. Instead embedded_object now has a release_from_owner method. +\li structs defined in type libraries now automatically use CoTaskMem when using new/delete. + + +\par Version 0.7.7 (2000-8-15): + +\li Changes to lifetime of embedded_object. Introducing tlb2h::untie, breaks tie between owner and embedded_object, so that the embedded_object can be destroyed before the owner. + + +\par Version 0.7.6 (2000-8-15): + +\li Various changes to comet::bstr_t. + + +\par Version 0.7.5 (2000-8-13): + +\li Interface inheritance problems solved. +\li Problem with [in] VARIANT arguments solved. +\li create_enum now works with embedded_objects. +\li comet::make_list now handles up to 40 elements. +\li Problem with IUnknown interfaces in coclasses solved. +\li Workaround for bug in Visual Studio editor causing VS to crash when editing certain template code. + + +\par Version 0.7.4 (2000-8-8): + +\li Conversion from comet::com_ptr to comet::variant_t improved. +\li Updated example to demonstrate now working COM enumeration implementation on STL containers. + + +\par Version 0.7.3 (2000-8-8): + +\li Nasty connection point bug fixed. +\li Added trivial VB client example program demonstrating connection point. +\li Added com_cast and try_cast for casting interface pointers. Implicit casting no longer allowed. +\li comet::com_error::what() is now working. +\li Misc. minor bug fixes/tweaks. + + +\par Version 0.7.2 (2000-8-3): + +\li Updated the example with registration custom build. +\li Misc. bug fixes. + +*/ +//! Primary namespace for comet. Includes source and libraries. +namespace comet { +//! Namespace for comet implementation details. +/*! \internal + */ +namespace impl { +} +} +/** \defgroup Misc Miscelaneous utility classes. + *@{ + */ +//@} +/** \defgroup COMType Com type wrappers. + *@{ + */ +//@} +/*!\defgroup WinUtil Windows utility classes. + *@{ + */ +//@} + + +#endif diff --git a/lib/local/CamCom/comet/common.h b/lib/local/CamCom/comet/common.h new file mode 100644 index 00000000..af06fd5b --- /dev/null +++ b/lib/local/CamCom/comet/common.h @@ -0,0 +1,316 @@ +/** \file + * Common utility classes wrappers. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_COMMON_H +#define COMET_COMMON_H + +#include +#include +#include + +namespace comet { + + class uuid_t; + templateclass com_ptr; + namespace impl { + template T* bad_alloc_check(T* x) + { + if (!x) throw std::bad_alloc(); + return x; + } + + /** Implementation struct for auto_attach. + * \internal + */ + template class auto_attach_t + { + public: + explicit auto_attach_t(const T& v) : val_(v) {}; + const T& get() const { return val_; } + private: + const T& val_; + auto_attach_t& operator=(const auto_attach_t&); + }; + + // Forward declare all these - only used if we actually WANT a + // safearray. + /** Safearray traits for the given type. + * \internal + */ + template struct sa_traits; + /** Safearray iterator for the given type. + * \internal + */ + template class sa_iterator; + /** Safearray const traits for the given type. + * \internal + */ + template struct const_traits; + /** Safearray non-const traits for the given type. + * \internal + */ + template struct nonconst_traits; + + enum sa_traits_check_type { stct_features_ok, stct_vt_ok, stct_iid_ok }; + enum sa_traits_extras_type { stet_null, stet_record, stet_iid }; + + // Interface traits are needed by all interfaces so that we can create + // safearrays of the types. + template + struct interface_sa_traits + { + enum { vt = VT }; + enum { check_type = stct_iid_ok }; + enum { extras_type = stet_iid }; + + typedef INTERFACE* raw; + typedef com_ptr value_type; + typedef com_ptr & reference; + typedef const com_ptr & const_reference; + + static reference create_reference(raw& x) { return *reinterpret_cast*>(&x); } + static const_reference create_const_reference(raw& x) { return *reinterpret_cast*>(&x); } + typedef nonconst_traits > nct; + typedef sa_iterator, nct > iterator; + typedef sa_iterator, const_traits > > const_iterator; + + static bool are_features_ok(unsigned short f) { return (f & FEATURE_FLAG) != 0 && (f & FADF_HAVEIID) != 0; } + static const uuid_t& iid() { return uuidof(); } + }; + + /** Basic safearray traits - used by enums. + * \internal + */ + template struct basic_sa_traits + { + enum { vt = VT }; + enum { check_type = stct_vt_ok }; + enum { extras_type = stet_null }; + + typedef T raw; + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; + + static reference create_reference(T& x) { return x; } + static const_reference create_const_reference(T& x) { return x; } + + typedef T* iterator; + typedef const T* const_iterator; + + static bool are_features_ok(unsigned short) { return true; } + }; + } + + /*! \addtogroup COMType + */ + //@{ + /// Used to attach a raw parameter to a wrapper. + template impl::auto_attach_t auto_attach(const T& t) { return impl::auto_attach_t(t); } + + +// template inline T up_cast(const U& u, T* = 0) { return u; } + + + /*! VARIANT_BOOL to bool [in] converter. + * This is used by the generated wrappers. + */ + inline VARIANT_BOOL bool_in(bool x) { return x ? COMET_VARIANT_TRUE : COMET_VARIANT_FALSE; } + + /** \class bool_out common.h comet/common.h + * VARIANT_BOOL to bool [out] converter. + * This is used by the generated wrappers. + */ + class bool_out { + public: + operator VARIANT_BOOL*() { return &vb_; } + bool_out(bool& b) : b_(b) {} + ~bool_out() { b_ = vb_ ? true : false; } + private: + bool_out &operator=( const bool_out &); + VARIANT_BOOL vb_; + bool& b_; + }; + + namespace impl { + class bool_adapter_t { + public: + bool_adapter_t(VARIANT_BOOL* p) : pb_(p) { b_ = *pb_ ? true : false; } + ~bool_adapter_t() { *pb_ = b_ ? COMET_VARIANT_TRUE : COMET_VARIANT_FALSE; } + + bool& ref() { return b_; } + private: + bool_adapter_t(const bool_adapter_t&); + bool_adapter_t& operator=(const bool_adapter_t&); + + VARIANT_BOOL* pb_; + bool b_; + }; + } + + /** \class bool_inout common.h comet/common.h + * VARIANT_BOOL to bool [in,out] converter. + * This is used by the generated wrappers. + */ + class bool_inout { + public: + operator VARIANT_BOOL*() { return &vb_; } + bool_inout(bool& b) : b_(b), vb_(b ? COMET_VARIANT_TRUE : COMET_VARIANT_FALSE) {} + ~bool_inout() { b_ = vb_ ? true : false; } + private: + bool_inout &operator=(const bool_inout &); + VARIANT_BOOL vb_; + bool& b_; + }; + + /** \class variant_bool_t common.h comet/common.h + * VARIANT_BOOL wrapper for structs. + * Stands in place of a VARIANT_BOOL in a struct, and behaves like a bool. + * This is imporant as sizeof(VARIANT_BOOL) != sizeof(bool). + */ + class variant_bool_t + { + VARIANT_BOOL vb_; + public: + /// \name Constructors. + //@{ + variant_bool_t(): vb_(COMET_VARIANT_FALSE) {} + variant_bool_t(const impl::auto_attach_t &b) : vb_(b.get()) {} + variant_bool_t(bool b) : vb_(b?COMET_VARIANT_TRUE:COMET_VARIANT_FALSE) {} + //@} + + /// \name Assignment operators. + //@{ + variant_bool_t &operator=( bool b) { vb_ = b?COMET_VARIANT_TRUE:COMET_VARIANT_FALSE; return *this;} + variant_bool_t &operator=( const impl::auto_attach_t &b) { vb_ = b.get(); return *this; } + //@} + + /// \name Boolean operators. + //@{ + operator bool() const{ return vb_!= COMET_VARIANT_FALSE; } + bool operator !() const { return vb_== COMET_VARIANT_FALSE; } + bool operator==( variant_bool_t vb) const { return vb.vb_ == vb_; } + bool operator!=( variant_bool_t vb) const { return vb.vb_ != vb_; } + //@} + + /// \name Bitwise operators + //@{ + variant_bool_t operator~() const { variant_bool_t temp(*this); temp.vb_ = (VARIANT_BOOL)~(temp.vb_); return temp; } + variant_bool_t &operator&=( const variant_bool_t &b) { vb_ &= b.vb_; return *this; } + variant_bool_t &operator|=( const variant_bool_t &b) { vb_ |= b.vb_; return *this; } + variant_bool_t &operator^=( const variant_bool_t &b) { vb_ ^= b.vb_; return *this; } + variant_bool_t operator&( const variant_bool_t &b)const { variant_bool_t temp(*this); temp.vb_ &= b.vb_; return temp; } + variant_bool_t operator|( const variant_bool_t &b)const { variant_bool_t temp(*this); temp.vb_ |= b.vb_; return temp; } + variant_bool_t operator^( const variant_bool_t &b)const { variant_bool_t temp(*this); temp.vb_ ^= b.vb_; return temp; } + //@} + /// \name bool operators + //@{ + bool operator&( bool b)const { return b & operator bool(); } + bool operator|( bool b)const { return b | operator bool(); } + bool operator^( bool b)const { return b ^ operator bool(); } + //@} + + static const variant_bool_t &create_const_reference(const VARIANT_BOOL &vb) { return reinterpret_cast(vb); } + static variant_bool_t &create_reference(VARIANT_BOOL &vb) { return reinterpret_cast(vb); } + + ///\name Raw accessors + //@{ + VARIANT_BOOL in() { return vb_; } + VARIANT_BOOL *out() { return &vb_; } + VARIANT_BOOL *inout() { return &vb_; } + //@} + + + /** Allow treating of class as a bool *. + * \sa bool_ptr() + */ + class bool_pointer_t + { + friend class variant_bool_t; + protected: + bool_pointer_t( VARIANT_BOOL &vb) : vb_(vb), b_( vb != COMET_VARIANT_FALSE) {} + public: + ~bool_pointer_t() { vb_ = b_ ? COMET_VARIANT_TRUE: COMET_VARIANT_FALSE; } + operator bool*(){ return &b_; } + operator const bool*()const{ return &b_; } + private: + bool_pointer_t &operator=(const bool_pointer_t &); + bool b_; + VARIANT_BOOL &vb_; + }; + /** Return a class that stands in for a bool *. + Should be used in place of operator & for passing in to a bool * function. + \code + variant_bool_t vb; + SomeFunc(vb.bool_ptr()); + \endcode + */ + bool_pointer_t bool_ptr() + { + return bool_pointer_t(vb_); + } + const bool_pointer_t bool_ptr() const + { + return bool_pointer_t(const_cast(vb_)); + } + + friend class bool_reference_t; + /** Allow efficient choosing between a bool& and a variant_bool&. + */ + class bool_reference_chooser_t + { + friend class variant_bool_t; + variant_bool_t &vbt_; + protected: + bool_reference_chooser_t(variant_bool_t &vbt):vbt_(vbt) {} + private: + bool_reference_chooser_t &operator=(const bool_reference_chooser_t &); + public: + inline operator variant_bool_t&() { return vbt_;} + inline operator const variant_bool_t&()const { return vbt_;} + }; + /** Allow treating of a class as a bool &. + * \sa bool_ref() + */ + class bool_reference_t : protected bool_pointer_t + { + public: + bool_reference_t( bool_reference_chooser_t &brc ) + : bool_pointer_t(*static_cast(brc).inout()) + {} + operator bool &(){ return tmp; /*return * (bool_pointer_t::operator bool*());*/ } + operator const bool &()const { return *(bool_pointer_t::operator const bool*()); } + bool tmp; + }; + + /** Return a class that stands in for a bool & or a variant_bool_t &. + */ + bool_reference_chooser_t bool_ref() + { + return bool_reference_chooser_t(*this); + } + const bool_reference_chooser_t bool_ref() const + { + return bool_reference_chooser_t(const_cast(*this)); + } + }; + //@} + +} // namespace + +#endif diff --git a/lib/local/CamCom/comet/config.h b/lib/local/CamCom/comet/config.h new file mode 100644 index 00000000..58b9dd3c --- /dev/null +++ b/lib/local/CamCom/comet/config.h @@ -0,0 +1,243 @@ +/** \file + * Included by all comet headers to provide compiler-specific + * configuration. + */ +/* + * Copyright 2000-2002 Sofus Mortensen + * Copyright (C) 2013 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_CONFIG_H +#define COMET_CONFIG_H + +#define COMET_MAJOR_VER 2 +#define COMET_BUILDTYPE gamma +#define COMET_MINOR_VER 1 +#define COMET_BUILD 20131017 + +#ifndef COMET_BUILD_VERSION_ONLY + +#ifndef COMET_NO_DECLSPEC_PROPERTY +#define COMET_ALLOW_DECLSPEC_PROPERTY +#endif // COMET_NO_DECLSPEC_PROPERTY + +#pragma warning(disable : 4786) +#pragma warning(disable : 4042) +#pragma warning(disable : 4290) +#pragma warning(disable : 4710) + +#ifdef _lint // PC/Lint has a few problems with comet. +# if !defined(_MSC_VER) || _MSC_VER >= 1300 +#define COMET_PARTIAL_SPECIALISATION +#else + +#endif +#define COMET_NESTED_TEMPLATES +#define COMET_GOOD_RECURSIVE_STRUCT +#define COMET_CONST_MEMBER_INIT +#define COMET_GUID_BASE GUID +#define COMET_STD_ITERATOR +#define COMET_NO_POINTER_CHECKING +#define COMET_STD_SWAP_NOTHROW throw() +#define COMET_TL_TRUNC +#else +//VC 7.1 support partial specialization +#ifdef _MSC_VER +#endif + +#ifdef __ICL +# define COMET_PARTIAL_SPECIALISATION +# define COMET_NESTED_TEMPLATES +#else +# ifdef _MSC_VER +# if _MSC_VER < 1300 +// Versions less than VC7 can't handle the rethrow & catch exception trick. +# define COMET_DISABLE_EXCEPTION_RETHROW_CATCH +# else // _MSC_VER >= 1300 +# define COMET_GOOD_RECURSIVE_STRUCT +# define COMET_CONST_MEMBER_INIT +# if _MSC_VER >= 1310 +# define COMET_PARTIAL_SPECIALISATION +# define COMET_NESTED_TEMPLATES +# endif +# endif + +# endif +#if defined(_UNICODE) && !defined(UNICODE) + #define UNICODE +#endif +#endif + +#ifdef __BORLANDC__ +#pragma warning(disable: 8027) +#pragma warning(disable: 8026) +#define COMET_PARTIAL_SPECIALISATION +#define COMET_NESTED_TEMPLATES +#define COMET_BROKEN_WTYPES +#define COMET_STD_ITERATOR +#define COMET_STD_SWAP_NOTHROW + +#define COMET_GUID_BASE _GUID +#else +#define COMET_STD_SWAP_NOTHROW throw() +#define COMET_GUID_BASE GUID +#endif + +#ifdef __CYGWIN__ +#define COMET_PARTIAL_SPECIALISATION +#define COMET_NESTED_TEMPLATES +#define COMET_GCC_HEADERS +#define COMET_STD_ITERATOR +#define COMET_TL_TRUNC +#define _T(x) x + +// This is not a good idea +// Routines that use _alloca has to be rewritten for __MINGW32__ targets. +// #define _alloca malloc + +// WIN32 defines for GCC +#define NONAMELESSUNION +#define NOCOMATTRIBUTE +#endif +#endif + + +// Use COMET_STRICT_TYPENAME only where MSVC barfs on stricter typename usage +// required by GCC. +#ifdef _MSC_VER +#if _MSC_VER <= 1300 +#define COMET_STRICT_TYPENAME +#else +#define COMET_STRICT_TYPENAME typename +#endif +#else +#define COMET_STRICT_TYPENAME typename +#endif + +// PRH Feb 26 2001 +// Try and determine which version of std::iterator we have. +// The ANSI/ISO compliant one seems to require a compiler +// that supports partial specialisation, as well as an +// ANSI/ISO compliant implementation of the library. + +// Unfortunately, there is no __ANSI_STL #define. +// So we guess: +// If we have partial specialisation, and we're using STLPort, then +// its the ANSI/ISO std::iterator, otherwise it is the MSVC 6.0 +// iterator. + +// If you're using some other decent STL other than STLPort, +// then you'll need to reflect this by ensuring that COMET_STD_ITERATOR +// is defined. + +#if _MSC_VER >= 1300 || defined(_CPPLIB_VER) +#define COMET_STD_ITERATOR +#else +#include +#ifdef COMET_PARTIAL_SPECIALISATION +// this code to "detect" STLport is stolen from +//#if __SGI_STL_PORT >= 0x400 || __SGI_STL_PORT >= 0x321 && defined(__STL_USE_NAMESPACES) +#ifdef __SGI_STL +#define COMET_STD_ITERATOR +#else +#ifdef __MINGW32__ +__STL_BEGIN_NAMESPACE +template struct iterator: bidirectional_iterator<_Tp,_Distance>{}; +__STL_END_NAMESPACE +#endif // __MINGW32__ +#endif +#else +#ifdef _CPPLIB_VER +#define COMET_STD_ITERATOR +#endif +#endif /* COMET_PARTIAL_SPECIALISATION */ +#endif + +#ifndef ATL_NO_VTABLE +#if _MSC_VER >= 1200 +#define ATL_NO_VTABLE __declspec(novtable) +#else +#define ATL_NO_VTABLE +#endif +#endif + +#if _MSC_VER <= 1200 +//#define COMET_DO_NOT_SPECIALISE_SWAP_IN_STD +#endif + +// Intel compiler version 500 sets _MSC_VER to 1200, +// but doesn't seem to like __forceinline +#if _MSC_VER >= 1200 && !defined(__ICL) +#define COMET_FORCEINLINE __forceinline +#else +#define COMET_FORCEINLINE inline +#endif + +#ifdef _UNICODE +#ifndef UNICODE +#define UNICODE // UNICODE is used by Windows headers +#endif +#endif + +#ifdef UNICODE +#ifndef _UNICODE +#define _UNICODE // _UNICODE is used by C-runtime/MFC headers +#endif +#endif + +// Remove Microsoft's C++ macros +#undef variant_t +#undef bstr_t +#undef com_cast + +// Stop subsequent includes of from #defining variant_t or bstr_t +#ifndef _USE_RAW +#define _USE_RAW +#endif + +#ifndef UUID_DEFINED +#define UUID_DEFINED +#endif + +#ifndef UUID +#define UUID GUID +#endif + +#ifdef uuid_t +#undef uuid_t +#endif + +#define COMET_DECLARE_SWAP(TYPE) template<> inline void swap(TYPE &x, TYPE &y)\ + {\ + x.swap(y);\ + } + +const unsigned short COMET_VARIANT_TRUE = (unsigned short)(0xffff); +const unsigned short COMET_VARIANT_FALSE = 0; + +#define COMET_NOTUSED(x) x +#ifndef COMET_CONST_MEMBER_INIT +# define COMET_CONST_TYPE(vartype, varname,value) enum { varname = value }; +# define COMET_CONST_GROUP enum { +# define COMET_CONST_ENTRY(vartype, varname,value) varname = value, +# define COMET_CONST_GROUP_END }; +#else +# define COMET_CONST_TYPE(vartype, varname,value) static const vartype varname = value; +# define COMET_CONST_ENTRY(vartype, varname,value) COMET_CONST_TYPE(vartype, varname, value); +# define COMET_CONST_GROUP +# define COMET_CONST_GROUP_END +#endif +#endif // COMET_BUILD_VERSION_ONLY +#endif + diff --git a/lib/local/CamCom/comet/cp.h b/lib/local/CamCom/comet/cp.h new file mode 100644 index 00000000..1c9a9e86 --- /dev/null +++ b/lib/local/CamCom/comet/cp.h @@ -0,0 +1,436 @@ +/** \file + * Connection-point implementations. + * The thought behind the current implementation was to try and maintain + * connections points accessible from the coclass through the member + * connection_point. + * + * Where multiple connection-points are defined, the member is accessed thus: + * \code + * connection_point_for::connection_point + * \endcode + * The default connection point wrapper implementations have a prefix of + * \b Fire_ before the event name. + */ +/* + * Copyright 2000 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_CP_H +#define COMET_CP_H + +#include + +#include + +#include +#include + +#pragma warning( push ) +#pragma warning( disable : 4355 ) + +/** \page cometconnectionpoints Connection Points. +\section cometconnectionpointssource Connction Point Source +Connection points provide events for comet, and rely on tlb2h (see \ref tlb2husage) to generate +the implementation for each method on the interface. + +Then comet::implement_cpc is used to provide IConnectionPointContainer +(which provides the method for finding a particular connection point. + +The default implementation of a coclass will inherit of this method by +default, however they can be explicitly referenced to provide +alternate implementations. + +The class comet::implement_cpc is templated to an interface list, which +can be constructed with comet::make_list. + +\code + class my_class : public simple_object< IFooImpl, implement_cpc< make_list::result > > +\endcode + +This causes each of the interface in the list of interfaces to be wrapped by a +comet::connection_point_for class which provides the access syntax. + +\code + connection_point_for::connection_point.Fire_FooMethod( args ); +\endcode + +Note that \b Fire_ is prepended to each method name. + +If there is only one connection point in the list, then the +connection_point_for namespace segregator is not required. + +If there is no connection point implementation for the interface, then you should make +sure that it is referenced with a [source] tag by a coclass definition +in the library you are including, and that you are generating server +implementations or forcing it by prepending '*' in a symbol file, see +the \ref wrappergensymbolformat. + +\section cometconnectionpointsink Connction Point Sink + +In order to prevent the circular references inherent in connection points, the +reference loop must be broken somehow. This is best done by using a contained +member that calls back on the parent class. + +The class sink_impl is best used in this circumstance as it contains code for calling +Advise and Unadvise. It inherits from static_object which is designed to be embedded in +a class, and does reference counting on the module and does not destruct itself. + +This class is templated to the interface implementation desired, which will +mostly be the 'Impl' class for the interface, but might be a raw COM interface. + +In this example we have a class 'MyCoclass' that is sinking the event interface IMyEventImpl which +has a single 'EventHappened' method. +\code + class coclass_MyCoclass : public coclass + { + // Private Embedded sink implementation. + struct sink_t : sink_impl + { + sink_t(coclass_MyCoclass *Parent) :m_parent(Parent) {} + inline void EventHappened( long param_Here ) { m_parent->sink_EventHappened(param_Here); } + coclass_MyCoclass *m_parent; // Non-addref reference. + } m_sink; + public: + coclass_MyCoclass() : m_sink(this) {} + + // Set the event source. + void SetEventSource( const com_ptr &unknown) + { + if (m_sink.is_advised()) + m_sink.unadvise(); + m_sink.advise( unknown ); + } + + inline void sink_EventHappened( long param_Here ) + { + // Event code goes here + } + }; +\endcode + +*/ + +namespace comet { + + template class connection_point; + + /*! \addtogroup Interfaces + */ + //@{ + /** \class simple_cpc cp.h comet/cp.h + * Implement a simple connection-point container with a single + * connection-point. + * \param Itf The single connection-point interface. + * \sa implement_cpc + */ + template class ATL_NO_VTABLE simple_cpc : public IConnectionPointContainer + { + public: + typedef IConnectionPointContainer interface_is; + + protected: + /// \name IConnectionPointContainer interface + //@{ + STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints**) + { + return E_NOTIMPL; + } + + STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint** ppCP) + { + if (!ppCP) return E_POINTER; + if (riid == uuidof()) { + *ppCP = &connection_point; + (*ppCP)->AddRef(); + return S_OK; + } + return CONNECT_E_NOCONNECTION; + } + //@} + + protected: + simple_cpc() : connection_point(this) {} + + connection_point connection_point; + }; + + + /** \class connection_point_for cp.h comet/cp.h + * Provide access to implementation for a connection point. + * \relates implement_cpc + */ + template class connection_point_for + { + public: + connection_point_for(::IUnknown *self) : connection_point(self) {} + connection_point connection_point; + }; + + + namespace impl { + + /** Find a connection point implementation for a given iid. + * \internal + */ + template struct connection_point_finder + { + template COMET_FORCEINLINE static ::IConnectionPoint* find_connection_point(T* This, const IID& iid) + { + typedef find_compatibility< COMET_STRICT_TYPENAME ITF_LIST::head > compatible; + if (iid == uuidof()) + return &((static_cast *>(This))->connection_point) ; + else return connection_point_finder::find_connection_point(This, iid); + } + }; + template<> struct connection_point_finder + { + template COMET_FORCEINLINE static ::IConnectionPoint* find_connection_point(T*, const IID&) + { + return 0; + } + }; + + } + + namespace impl { + + + template struct ATL_NO_VTABLE inherit_all_ex_unknown; + +#ifdef COMET_GOOD_RECURSIVE_STRUCT + // Remove level of indirection. PC-lint cannot handle it, and MSVC7 + // should be ale to. + template struct ATL_NO_VTABLE inherit_all_ex_unknown + : public ITF_LIST::head, public inherit_all_ex_unknown + { + inherit_all_ex_unknown( ::IUnknown *initParam) + : ITF_LIST::head(initParam), inherit_all_ex_unknown< COMET_STRICT_TYPENAME ITF_LIST::tail >(initParam) + {} + }; + template<> struct inherit_all_ex_unknown { inherit_all_ex_unknown(::IUnknown *) {} }; + +#else // COMET_GOOD_RECURSIVE_STRUCT + template struct ATL_NO_VTABLE inherit_all_ex_aux_unknown + : public HEAD, public inherit_all_ex_unknown + { + inherit_all_ex_aux_unknown( ::IUnknown *initParam) + : HEAD(initParam), inherit_all_ex_unknown(initParam) + {} + }; + +// COMET_CONFIG_H is always defined! This is just a trick to get Doxygen to ignore the following declaration that +// otherwise seems to be cause an exception in Doxygen 1.2.8 +#ifdef COMET_CONFIG_H + template struct ATL_NO_VTABLE inherit_all_ex_unknown + : public inherit_all_ex_aux_unknown + { + inherit_all_ex_unknown( ::IUnknown *initParam) + :inherit_all_ex_aux_unknown< COMET_STRICT_TYPENAME ITF_LIST::head, COMET_STRICT_TYPENAME ITF_LIST::tail >(initParam) + { + } + }; + template<> struct inherit_all_ex_unknown { inherit_all_ex_unknown(::IUnknown *) {} }; +#endif // COMET_CONFIG_H +#endif // COMET_GOOD_RECURSIVE_STRUCT + + + + } + + + COMET_WRAP_EACH_DECLARE( connection_point_for) + + + /** \struct implement_cpc cp.h comet/cp.h + * Implement a connection point container that can handle multiple + * connection points. + * This should be added to the \link comet::make_list list \endlink of implemented interfaces for a coclass as it + * implements IConnectionPointContainer (which will be required for a qi). + * + * The class is used by the default coclass implementation to provide \ref cometconnectionpoints. + * + * \param ITF_LST \link comet::make_list List \endlink of connection points interfaces to implement. + */ + template< typename ITF_LST> struct ATL_NO_VTABLE implement_cpc : public IConnectionPointContainer + , public impl::inherit_all_ex_unknown< COMET_WRAP_EACH(connection_point_for, ITF_LST) > + { + typedef IConnectionPointContainer interface_is; + public: + implement_cpc() + : impl::inherit_all_ex_unknown< COMET_WRAP_EACH(comet::connection_point_for, ITF_LST) >((::IUnknown *)this) + {} + + protected: + friend struct impl::connection_point_finder; + /// \name IConnectionPointContainer interface + //@{ + STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints**) + { + return E_NOTIMPL; + } + + STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint** ppCP) + { + if (!ppCP) return E_POINTER; + + const IID& iid = riid; + + *ppCP = impl::connection_point_finder::find_connection_point(this, iid); + + if ( *ppCP !=NULL) + { + (*ppCP)->AddRef(); + return S_OK; + } + return CONNECT_E_NOCONNECTION; + } + //@} + }; + + + /** \class connection_point_impl cp.h comet/cp.h + * Implements a connection point. + * \param Itf Interface of connection point. + */ + template class ATL_NO_VTABLE connection_point_impl : public embedded_object< IUnknown, IConnectionPoint > + { + public: + bool is_connected() const + { return !connections_.empty(); } + protected: + connection_point_impl(::IUnknown* pUnk) : next_cookie_(1), embedded_object< IUnknown, IConnectionPoint >(pUnk) {} + + /// \name IConnectionPoint interface + //@{ + STDMETHOD(GetConnectionInterface)(IID* pIID) + { + *pIID = uuidof(); + return S_OK; + } + + STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer** ppCPC) { + com_ptr p; + p = try_cast(com_ptr< ::IUnknown >(get_parent())); + *ppCPC = com_ptr::detach( p ); + return S_OK; + } + + STDMETHOD(Advise)(::IUnknown* pUnkSink, DWORD* pdwCookie) + { + try { + connections_[next_cookie_] = try_cast( com_ptr< ::IUnknown >(pUnkSink) ); + } + catch (...) { + return CONNECT_E_CANNOTCONNECT; + } + *pdwCookie = next_cookie_++; + return S_OK; + } + STDMETHOD(Unadvise)(DWORD dwCookie) + { + CONNECTIONS::iterator it = connections_.find(dwCookie); + if (it == connections_.end()) return CONNECT_E_NOCONNECTION; + connections_.erase(it); + return S_OK; + } + + STDMETHOD(EnumConnections)(IEnumConnections** ppEnum) + { + try { + *ppEnum = com_ptr::detach( stl_enumeration::create(connections_, get_unknown()) ); + } catch (...) { + return E_FAIL; + } + return S_OK; + } + //@} + + typedef std::map > CONNECTIONS; + CONNECTIONS connections_; + private: + UINT next_cookie_; + }; + + /** \class sink_impl cp.h comet/cp.h + * Implement a sink for a connection pointer. + * \param Itf interface to implement. + */ + template class ATL_NO_VTABLE sink_impl : public static_object + { + public: + /** Advise this object as sinking connections from \p t. + */ + void advise(const com_ptr< ::IUnknown>& t) + { + if (ptr_) throw std::runtime_error("Cannot double advise."); + com_ptr cpc( try_cast(t) ); + IConnectionPoint* cp; + cpc->FindConnectionPoint( uuidof(), &cp) | raise_exception; + + HRESULT hr = cp->Advise(static_cast< ::IUnknown* >(static_cast(this)), &cookie_); + + cp->Release(); + + hr | raise_exception; + + ptr_ = t; + } + + /** Unadvise this interface from object \p t. + */ + void unadvise() + { + if (ptr_) { + com_ptr cpc( try_cast(ptr_) ); + IConnectionPoint* cp; + cpc->FindConnectionPoint( uuidof(), &cp) | raise_exception; + + HRESULT hr = cp->Unadvise(cookie_); + cookie_ = 0; + ptr_ = 0; + + cp->Release(); + + hr | raise_exception; + } + } + + /** Get event object. + */ + com_ptr< ::IUnknown> object() + { + return ptr_; + } + + /** Return true if advised. + */ + bool is_advised() + { + return !ptr_.is_null(); + } + + protected: + sink_impl() : cookie_(0) {} + ~sink_impl() { unadvise(); } + private: + DWORD cookie_; + com_ptr< ::IUnknown> ptr_; + }; + //@} + +} + +#pragma warning( pop ) + +#endif diff --git a/lib/local/CamCom/comet/cptraits.h b/lib/local/CamCom/comet/cptraits.h new file mode 100644 index 00000000..539b4e8c --- /dev/null +++ b/lib/local/CamCom/comet/cptraits.h @@ -0,0 +1,118 @@ +/** \file + * Connection-point traits. + */ + +/* Copyright 2002 Michael Geddes + * Copyright 2013 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef INCLUDE_COMET_CPTRAITS_H +#define INCLUDE_COMET_CPTRAITS_H + +#ifdef _SHOW_INC +#pragma message(" #Include " __FILE__) +#endif + +#include +#include // raise_exception + +#include // HRESULT + +namespace comet +{ + /*! \struct cp_throw cptraits.h comet/cptraits.h + * Default 'throw' traits for connection-points. + * \code Usage + * connection_point_for::connection_point.Fire_MyEvent( arg1, comet::cp_throw()); + * \endcode + */ + struct cp_throw + { + + /// Constructor - called before iterating over the connection-points. + cp_throw() : _hr_(S_OK) { } + + /// Destructor - called after iterating over all the connection-points. + ~cp_throw() { _hr_ | raise_exception ; } + + /*! Called when a connection point fails. + * Can also be used to remember the hresult for the destructor. + * + * \retval true Cause the on_fail method to be called. + * \retval false Ignore the failure. + */ + bool check_fail(HRESULT hr) + { + if( FAILED(hr) ) + _hr_ = hr; + return false; + } + + /*! Called when check_fail returns true. + * \param par_connects Reference to the STL container containing the connection-points. + * \param par_it The iterator of the failed connection-point. + */ + template + static bool on_fail(CONNECTIONS &par_connects, const typename CONNECTIONS::iterator & par_it) + { + COMET_NOTUSED(par_it); + COMET_NOTUSED(par_connects); + return false; + } + + protected: + HRESULT _hr_; + + }; + + /*! \struct cp_nothrow_remove cptraits.h comet/cptraits.h + * Traits for connection-points, errors cause the connection-point to auto-remove. + * \code + * connection_point_for::connection_point.Fire_MyEvent( arg1, comet::cp_nothrow_remove()); + * \endcode + * \sa cp_throw + */ + struct cp_nothrow_remove + { + //! Called when a connection point fails. + bool check_fail( HRESULT _hr_) throw() { return FAILED(_hr_); } + + //! Called when check_fail returns true. + template + static bool on_fail(CONNECTIONS &par_connects, const typename CONNECTIONS::iterator &par_it ) throw() + { + try{ par_connects.erase(par_it); }catch(...) {} + return false; + } + }; + + + /*! \struct cp_nothrow cptraits.h comet/cptraits.h + * Tratis for connection-points, errors are ignored. + * \code + * connection_point_for::connection_point.Fire_MyEvent( arg1, comet::cp_nothrow()); + * \endcode + * \sa cp_throw + */ + struct cp_nothrow + { + //! Called when a connection point fails. + bool check_fail( HRESULT _hr_) throw() { return FAILED(_hr_); } + + //! Called when check_fail returns true. + template + static bool on_fail(CONNECTIONS &par_connects, const typename CONNECTIONS::iterator &par_it ) throw() { return false; } + }; +} +#endif /* INCLUDE_COMET_CPTRAITS_H */ diff --git a/lib/local/CamCom/comet/currency.h b/lib/local/CamCom/comet/currency.h new file mode 100644 index 00000000..567f93d0 --- /dev/null +++ b/lib/local/CamCom/comet/currency.h @@ -0,0 +1,474 @@ +/** \file + * Currency wrapper. + */ +/* Copyright 2001 Michael Geddes, Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_CURRENCY_H +#define COMET_CURRENCY_H + +#include + +#include +#include + +#include + +#include +#include + +namespace comet +{ + + class bstr_t; + + // currency_t + /////////////// + + /*! \addtogroup COMType + */ + //@{ + + /** Wrapper for CURRENCY type. + * CURRENCY is a fixed point (to 4 decimal places) 64 bit value. + */ + + class currency_t + { + public: + /// Default Constructor + currency_t() throw() { cy_.int64 = 0; } + + /// CY constructor. + currency_t(const CY &cy): cy_(cy) { } + + /// Double Conversion constructor. + explicit currency_t(double val) + { + VarCyFromR8(val,&cy_) | raise_exception; + } + +#if 0 +// //! Construct currency from CY/CURRENCY. +// /*! +// Takes ownership of specified CY value. To prevent misuse the CY must be wrapped using auto_attach. +// +// \code +// currency_t cy( auto_attach( myCY ) ); +// \endcode +// +// \param cyVal +// Value to initialise currency_t from. +// */ +// currency_t( const impl::auto_attach_t &cyVal) throw() : cy_(cyVal.get()) +// {} +#endif //0 + + /// Long Conversion constructor. + currency_t( long val ) + { + VarCyFromI4(val,&cy_) | raise_exception; + } + + currency_t( int val ) + { + VarCyFromI4(val,&cy_) | raise_exception; + } + + void swap(currency_t& c) throw() + { + std::swap(cy_, c.cy_); + } + + static const currency_t& create_const_reference(const CY& s) throw() + { return *reinterpret_cast(&s); } + + static currency_t& create_reference(CY& s) throw() + { return *reinterpret_cast(&s); } + + //! \name Assignment Operators + //@{ + currency_t &operator=(double newVal) + { + currency_t c( newVal ); + swap(c); + return *this; + } + currency_t &operator=(long newVal) + { + currency_t c( newVal ); + swap(c); + return *this; + } + currency_t &operator=(int newVal) + { + currency_t c( newVal ); + swap(c); + return *this; + } + currency_t &operator=(const tagCY &newVal) throw() + { + cy_ = newVal; + return *this; + } + //@} + + /// \name Mathematical Operators + //@{ + currency_t &operator+=(const currency_t &cy) + { + currency_t c( *this + cy ); + swap(c); + return *this; + } + currency_t &operator-=(const currency_t &cy) + { + currency_t c( *this - cy ); + swap(c); + return *this; + } + currency_t &operator*=(const currency_t &cy) + { + currency_t c( *this * cy ); + swap(c); + return *this; + } + currency_t &operator*=(long cy) + { + currency_t c( *this * cy ); + swap(c); + return *this; + } + currency_t &operator*=(int val) + { + return operator*=((long)val); + } + currency_t &operator*=(double val) + { + cy_.int64 = (LONGLONG)(cy_.int64 * val); + return *this; + } + + currency_t &operator/=(int val) const + { + return operator/=((long)val); + } + currency_t &operator/=(long val) + { + + if(!valid()) + { + // Check for invalid number + return *this; + } + + // Check for divide by 0 + if (val == 0) + { + // Set to maximum negative value + cy_.Hi = 0x80000000; + cy_.Lo = 0x00000000; + + return *this; + } + cy_.int64/=val; + + return *this; + } + + currency_t operator+(const currency_t &cy)const + { + currency_t rv; + VarCyAdd(cy_,cy.cy_,&rv.cy_) | raise_exception ; + return rv; + } + currency_t operator-(const currency_t &cy)const + { + currency_t rv; + VarCySub(cy_,cy.cy_,&rv.cy_) | raise_exception ; + return rv; + } + currency_t operator*(const currency_t &cy)const + { + currency_t rv; + VarCyMul(cy_,cy.cy_,&rv.cy_) | raise_exception ; + return rv; + } + currency_t operator*(long cy)const + { + currency_t rv; + VarCyMulI4(cy_,cy,&rv.cy_) | raise_exception; + return rv; + } + currency_t operator*(int cy)const + { + return operator *((long)cy); + } + + currency_t operator*(double cy) const + { + currency_t val(*this); + val *=cy; + return val; + } + + + /// Calculate approximate ratio. + double operator/(const currency_t &cy)const + { + return ((double)cy_.int64 /(double)cy.cy_.int64); + } + + /// Divide by int. + currency_t operator/(int val) const + { + return operator/((long)val); + } + /// Divide by long. + currency_t operator/(long val) const + { + currency_t tmp(cy_); + tmp/=val; + return tmp; + } + + /// Divide by double + double operator/(double val) const + { + if(!valid()) + { + // Check for invalid number + throw std::invalid_argument("Invalid divide"); + } + + // Check for divide by 0 + if (val == 0) + { + throw std::overflow_error("Divide by 0"); + } + return cy_.int64/(val*10000); + } + + /// Unary negate. + currency_t operator-()const + { + currency_t cy; + VarCyNeg(cy_,&(cy.cy_)) | raise_exception; + return cy; + } + + //@} + + /** Rounds the value to specified number of decimal places. + * \param decimals Number of places to round to. + */ + currency_t &round_to(int decimals) + { + VarCyRound(cy_,decimals,&cy_) | raise_exception; + return *this; + } + + /// \name Logical Operators + //@{ + + bool operator!=(const currency_t &cy) const { return cmp(cy)!=0; } + bool operator!=(double val) const{ return cmp(val)!=0; } + bool operator==(const currency_t &cy) const { return cmp(cy)==0; } + bool operator==(double val) const{ return cmp(val)==0; } + bool operator<=(const currency_t &cy) const{ return cmp(cy)<=0; } + bool operator<=(double val) const{ return cmp(val)<=0; } + bool operator>=(const currency_t &cy) const{ return cmp(cy)>=0; } + bool operator>=(double val) const{ return cmp(val)>=0; } + bool operator<(const currency_t &cy) const{ return cmp(cy)<0; } + bool operator<(double val) const{ return cmp(val)<0; } + bool operator>(const currency_t &cy) const{ return cmp(cy)>0; } + bool operator>(double val) const{ return cmp(val)>0; } + + //@} + + /** Compares the value like strcmp. + * \param cy Number to be compared. + */ + int cmp(const currency_t &cy) const + { + return _cmpResult(VarCyCmp(cy_,cy.cy_)); + + } + + /** Compares the value like strcmp. + * \param cy Number to be compared. + */ + int cmp(double cy) const + { + return _cmpResult(VarCyCmpR8(cy_,cy)); + } + + + //! \name Access converters + //@{ + tagCY get() const { return cy_;} + tagCY in() const { return cy_;} + tagCY *in_ptr() const { return const_cast(&cy_);} + tagCY *out() { return &cy_;} + tagCY *inout() { return &cy_;} + //@} + + +#if 0 + friend std::ostream &operator <<(std::ostream &str, const currency_t &val) + { + std::string strval=val.format( 1, str.precision(), str.width() ); + return str<< strval.c_str(); + } +#endif + + + + friend + std::basic_ostream &operator<<(std::basic_ostream &str, const currency_t &val) + { + std::basic_string strval; + val.do_format(strval, 1, str.precision(), str.width() ); + return str << strval.c_str(); + } + + friend + std::basic_ostream &operator<<(std::basic_ostream &str, const currency_t &val) + { + std::basic_string strval; + val.do_format(strval, 1, str.precision(), str.width() ); + return str << strval.c_str(); + } + + //! Format the string with the given digits, precision and width. + std::basic_string format( + std::streamsize mindigits=0, std::streamsize minprecision=0, + std::streamsize width=0) const + { + std::basic_string strval; + do_format(strval, mindigits, minprecision, width); + return strval; + } + + //! Parse the string to a currency. + currency_t &parse( const bstr_t &str, LCID locale =::GetThreadLocale() ); +/* { + VarCyFromStr( str.in(), locale, 0, &cy_ ) | raise_exception; + return *this; + }*/ + + protected: + /** Return a string representation of the value. + * \param val output string (return values can't automatically detect template arguments) + * \param mindigits Minimum number before decimal point. + * \param minprecision Minimum number after decimal point. + * \todo Obey ostream formats for: fillchar(), ios_base::left, ios_base::internal, ios_base::showpos + */ + template + void do_format( + std::basic_string& val, std::streamsize mindigits, + std::streamsize minprecision, std::streamsize /*width*/) const + { + COMET_ASSERT(mindigits>=0 && minprecision >=0 ); + if(minprecision> 4) minprecision =4; + + // Add in the 4 fixed decimal points + std::streamsize pr = + ((0 <= minprecision && minprecision <=4) ? + (4-minprecision) : 0); + mindigits+=4; + + val.erase(); + val.reserve(22); + LONGLONG value=cy_.int64; + bool neg=value<0; + if(neg) + { + value=-value; + } + // Put in the digits backwards + std::streamsize digit=0; + bool output=false; + while(value !=0 || digit < mindigits) + { + CH dig=CH(value%10); + if(output || true==(output= (dig >0 || digit>=pr))) // Once 'output' is set - output everything. + { + val+=(CH('0'+dig)); + } + if(++digit == 4) + { + val+=CH('.'); + output=true; + } + value/=10; + } + if(neg) + { + val+=CH('-'); + } + + // Now reverse the digits + std::reverse(val.begin(), val.end()); + } + public: + + /// Returns true if this is a valid number + bool valid() const throw() + { return !(cy_.Hi==(long)0x80000000 && cy_.Lo==0);} + + /// Detaches the CY value. Provided for consistancy. + CY detach() throw() + { + CY val = cy_; + cy_.int64 = 0; + return val; + } + + /// Detaches the CY value. Provided for consistancy. + static CY detach(currency_t& cy) throw() + { + return cy.detach(); + } + + private: + CY cy_; + + static int _cmpResult(HRESULT hr) + { + if(SUCCEEDED(hr)) + { + switch(hr) + { + case VARCMP_LT: + return -1; + case VARCMP_EQ : + return 0; + case VARCMP_GT: + return 1; + case VARCMP_NULL: + COMET_ASSERT(!"What do we do with this?"); + } + } + else + { + hr | raise_exception; + } + return 0; // shut the compiler up + } + }; + //@} +} + +#endif // COMET_CURRENCY_H diff --git a/lib/local/CamCom/comet/date.h b/lib/local/CamCom/comet/date.h new file mode 100644 index 00000000..e577cace --- /dev/null +++ b/lib/local/CamCom/comet/date.h @@ -0,0 +1,396 @@ +/** \file + * Date wrapper (part of datetime). + */ +/* +* Copyright 2004 Michael Geddes +* +* This material is provided "as is", with absolutely no warranty +* expressed or implied. Any use is at your own risk. Permission to +* use or copy this software for any purpose is hereby granted without +* fee, provided the above notices are retained on all copies. +* Permission to modify the code and to distribute modified code is +* granted, provided the above notices are retained, and a notice that +* the code was modified is included with the above copyright notice. +* +* This header is part of Comet version 2. +* https://github.com/alamaison/comet +*/ + +#ifndef INCLUDE_COMET_DATE_H +#define INCLUDE_COMET_DATE_H + +#ifdef _SHOW_INC +#pragma message(" #Include " __FILE__) +#endif +#include + +namespace comet +{ + /*! \addtogroup Misc + */ + //@{ + + //! Wrapper for a date only class. + /**This is based on an integer version of DATE, except that it is an + * absolute offset from 1/1/0. + */ + struct dateonly_t : protected impl::datetime_base + { + + /// constructor + dateonly_t() { dt_ = 0; } + + /// Construct from a datetime_t. + explicit dateonly_t( const datetime_t &dt) + { + if (dt == dt_invalid ) + dt_ = dt_invalid_; + else if (dt == dt_null) + dt_ = dt_null_; + else + dt_ = split_oledate_as_absdate_( dt.get()); + } + + /// Attach from a long. + dateonly_t( const impl::auto_attach_t &dateval) + { + dt_ = dateval.get(); + } + + /// Get the raw 'long' value. + long get() const { return dt_; } + dateonly_t &operator=(const impl::auto_attach_t & dateval) + { + dt_ = dateval.get(); + return *this; + } + + /// Initialise as invalid. + dateonly_t( dt_invalid_t ) { dt_ = dt_invalid_; } + /// Initialise as null. + dateonly_t( dt_null_t ) { dt_ = dt_null_; } + /// Initialise as zero. + dateonly_t( dt_zero_t) { dt_ = 0; } + + + /// Construct from year/month/day. + dateonly_t( int year, int month, int day ) + { + if (!absdate_from_date_( &dt_, year, month, day)) + set_invalid_(); + } + + /// Get a 'NULL' datetime. + static dateonly_t get_null() { return dateonly_t( dt_null_ ); } + /// Get a 'zero' datetime. + static dateonly_t get_zero() { return dateonly_t( 0 ); } + + /// Return today. + static dateonly_t today() + { + return dateonly_t(datetime_t::now()); + } + /// Return today (UTC). + static dateonly_t today_utc() + { + return dateonly_t(datetime_t::now_utc()); + } + + /// Return year/month/day values. + void split(int *year, int *month, int *day) const + { + if(!good() || ! date_from_absdate_( dt_, year, month, day)) + throw datetime_exception("Invalid Date"); + } + + /** Set date part as year/month/day. + * \param year Year (from year 0 - as in 2000). + * \param month Month of year (1-based). + * \param day Day of month (1-based). + * \retval true Successfully set date. + * \retval false Conversion unsuccessful - date not set. + */ + bool set_date( int year, int month, int day) + { + return absdate_from_date_( &dt_, year, month, day); + } + + /// Convert to datetime. + operator datetime_t() const + { + if (!good()) return datetime_t((DATE)dt_); + return datetime_t(join_absdate_as_oledate_( dt_, 0)); + } + + /// \name Access date parts. + //@{ + /// Year. + int year() const + { + int year; + split(&year,NULL,NULL); + return year; + } + /// Month of year (1-based) + int month() const + { + int year,month,day; + split(&year,&month,&day); + return month; + } + /// Day of month (1-based) + int day() const + { + int year,month,day; + split(&year,&month,&day); + return day; + } + /// The day of week. + datetime_t::day_of_week dow() const + { + int wday; + if(!good() || ! dow_from_absdate_( dt_, &wday)) + throw datetime_exception("Invalid Date"); + return datetime_t::day_of_week(wday); + } + /// Day of the year (0 -based) + int year_day() const + { + if (good()) + { + int y,m,d; + date_from_absdate_(dt_, &y,&m,&d); + long firstday; + if ( absdate_from_date_(&firstday, y, 1, 1)) + return 1 + ( dt_ - firstday); + } + throw datetime_exception("Invalid Date"); + } + /// Days in the month. + int days_in_month() const + { + int year,month,day; + split(&year,&month,&day); + return days_in_month(year,month); + } + + //@} + + /** Add specified number of months. + * If the day is not valid, force to the last day in the month. + */ + dateonly_t &add_months(int inc_months) + { + int year,month,day; + split(&year,&month,&day); + long months = (month-1)+(year*12)+inc_months; + + long quot,rem; + quot = months/12; + rem = months%12; + set_date( quot, rem+1, day); + return *this; + } + + /// Add specified number of years. + dateonly_t &add_years(int inc_years) + { + int year,month,day; + split(&year,&month,&day); + set_date( year+inc_years, month, day); + return *this; + } + + ///\name Comparison operators + //@{ + bool operator==(const dateonly_t& date) const { return date.dt_ == dt_; } + bool operator!=(const dateonly_t& date) const{ return date.dt_ != dt_; } + bool operator<(const dateonly_t& date) const + { + COMET_ASSERT( good() ); + COMET_ASSERT( date.good() ); + return dt_ < date.dt_; + } + bool operator>(const dateonly_t& date) const + { + COMET_ASSERT( good() ); + COMET_ASSERT( date.good() ); + return dt_ > date.dt_; + } + bool operator<=(const dateonly_t& date) const + { + COMET_ASSERT( good() ); + COMET_ASSERT( date.good() ); + return dt_ <= date.dt_; + } + bool operator>=(const dateonly_t& date) const + { + COMET_ASSERT( good() ); + COMET_ASSERT( date.good() ); + return dt_ >= date.dt_; + } + bool operator==(dt_invalid_t) const { return invalid(); } + bool operator!=(dt_invalid_t) const { return !invalid(); } + bool operator==(dt_zero_t) const { return dt_==0; } + bool operator!=(dt_zero_t) const { return dt_!=0; } + bool operator==(dt_null_t) const { return null(); } + bool operator!=(dt_null_t) const { return !null(); } + //@} + + ///\name Arithmetic operators + //@{ + dateonly_t operator+(long dateSpan) const + { + dateonly_t dt(*this); + dt+=dateSpan; + return dt; + } + dateonly_t operator-(long dateSpan) const + { + dateonly_t dt(*this); + dt-=dateSpan; + return dt; + } + dateonly_t& operator+=(long dateSpan) + { + COMET_ASSERT( good() ); + dt_ += dateSpan; + return *this; + } + dateonly_t& operator-=(long dateSpan) + { + COMET_ASSERT( good() ); + dt_ -= dateSpan; + return *this; + } + long operator-(const dateonly_t& date) const + { + COMET_ASSERT( good() ); + COMET_ASSERT( date.good() ); + return dt_ - date.dt_; + } + dateonly_t &operator++() + { + COMET_ASSERT( good() ); + ++dt_; + return *this; + } + dateonly_t operator++(int) + { + COMET_ASSERT( good() ); + dateonly_t t(*this); + ++dt_; + return t; + } + dateonly_t &operator--() + { + COMET_ASSERT( good() ); + --dt_; + return *this; + } + dateonly_t operator--(int) + { + COMET_ASSERT( good() ); + dateonly_t t(*this); + --dt_; + return t; + } + //@} + + /// return true if the date is marked 'invalid'. + inline bool invalid() const { return dt_ == ( dt_invalid_); } + /// Return true if the date is marked 'null' + inline bool null() const { return dt_ == ( dt_null_); } + + /// return true if date is zero + inline bool zero() const { return dt_ == 0; } + + /// Return true if the date is usable. + inline bool good() const + { + switch (dt_) + { + case 0: case dt_invalid_: case dt_null_: return false; + default: return true; + } + } + + /** Format as bstr. + * \param flags Format (can be or-ed). All format_flags are valid. + * \param locale Locale ID (default to User Default. + */ + bstr_t format( datetime_t::format_flags flags = datetime_t::ff_default , LCID locale = LOCALE_USER_DEFAULT) const + { + flags = datetime_t::format_flags((flags | datetime_t::ff_date_only) & ~datetime_t::ff_time_only); + bstr_t strDate; + if (null() || invalid()) + return strDate; + + DATE dt = join_absdate_as_oledate_( dt_, 0); + VarBstrFromDate(dt, locale, flags, strDate.out()) | raise_exception; + return strDate; + } + + /** Parse bstring to a datetime_t. + * \param val String to parse. + * \param flags valid format_flags are: ff_default, ff_system_locale, ff_hijri, ff_time_only, ff_date_only + * \param locale Locale to use. Default \p locale is the user default. + */ + dateonly_t &parse( const bstr_t &val, datetime_t::format_flags flags = datetime_t::ff_default, LCID locale = LOCALE_USER_DEFAULT) + { + flags = datetime_t::format_flags((flags | datetime_t::ff_date_only) & ~datetime_t::ff_time_only); + DATE dt; + VarDateFromStr( val.in(), locale, flags, &dt) | raise_exception; + dt_ = split_oledate_as_absdate_( dt); + + return *this; + } + + /** Format as basic_string. + * \param fmt See system documentation for strftime for details. + */ + template + std::basic_string format( const std::basic_string &fmt ) const + { + return format(fmt.c_str()); + } + + /** Format as basic_string. + * \param fmt See system documentation for strftime for details. + */ + template + std::basic_string format( const CHAR *fmt ) const + { + // Just use datetime_t to handle this. + datetime_t dt(*this); + return dt.format(fmt); + } + + /// Stream operator. + friend + std::basic_ostream &operator<<(std::basic_ostream &os, const dateonly_t &val) + { + os << val.format(); + return os; + } + + /// Stream operator. + friend + std::basic_ostream &operator<<(std::basic_ostream &os, const dateonly_t &val) + { + os << val.format(); + return os; + } + + static inline int days_in_month(int year, int month) + { + return impl::datetime_base::days_in_month(year,month); + } + + protected: + explicit dateonly_t(long l) { dt_ = l; } + }; + //@} +}; + +#endif /* INCLUDE_COMET_DATE_H */ diff --git a/lib/local/CamCom/comet/datetime.h b/lib/local/CamCom/comet/datetime.h new file mode 100644 index 00000000..351a9d19 --- /dev/null +++ b/lib/local/CamCom/comet/datetime.h @@ -0,0 +1,1943 @@ +/** \file + * Wrappers for DATE. + */ + +/* Copyright 2001 Michael Geddes + * + * This class was originally based on ATL/MFC code, however the original + * implementations have almost entirely been replaced with more efficient code. + * The core date algorithms are from boost. + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ +#ifndef COMET_DATETIME_H +#define COMET_DATETIME_H + +#include +#include +#include + +#include +#include + +// The Platform SDK does not define VAR_FOURDIGITYEARS +#ifndef VAR_FOURDIGITYEARS +#define VAR_FOURDIGITYEARS ((DWORD)0x00000040) +#endif + +namespace comet +{ + +#define COMET_DIVMOD_( quot,rem, val1, val2) quot = (val1)/(val2); rem = (val1)%(val2); + + +/*! \addtogroup ErrorHandling + */ +//@{ +//! Exception for datetimes. +class datetime_exception : public std::exception +{ +public: + datetime_exception( const char *desc) : desc_(desc) + {} + + const char* what() const throw() + { + return desc_.c_str(); + } + +private: + std::string desc_; +}; + +//@} + +/*! \addtogroup COMType + */ +//@{ +/// Initialise date/time value as invalid. +static struct dt_invalid_t {} dt_invalid; +/// Initialise date/time value as null. +static struct dt_null_t {} dt_null; +/// Initialise date/time value as zero. +static struct dt_zero_t { + operator double() const { return 0.;} + operator long() const { return 0;} +} dt_zero; + +// timeperiod_t +///////////////// + +/** \class timeperiod_t datetime.h comet/datetime.h + * Time-period. Used with datetime_t math. + */ +class timeperiod_t +{ + enum { + dt_invalid_ = 2147483647L, + }; + /// Days. + double pd_; +public: + /// Default Constructor. + timeperiod_t() : pd_(0.){} + + /// Construct invalid + timeperiod_t( dt_invalid_t ) : pd_(dt_invalid_) {} + /// Construct zero + timeperiod_t( dt_zero_t) : pd_(0.) {} + + timeperiod_t( double period) :pd_(period){} + timeperiod_t( float period) :pd_(period){} + timeperiod_t( long period) :pd_(period){} + timeperiod_t( int period) :pd_(period){} + timeperiod_t( short period) :pd_(period){} + timeperiod_t( unsigned long period) :pd_(period){} + timeperiod_t( unsigned int period) :pd_(period){} + timeperiod_t( unsigned short period) :pd_(period){} + + timeperiod_t( long days, long hours) + : pd_(days + hours/24){} + timeperiod_t( long days, long hours, long minutes) + : pd_(days + (hours*60+minutes)/(24.*60.)){} + timeperiod_t( long days, long hours, long minutes, long seconds, long milliseconds=0 ) + : pd_(days + ((hours*3600000L) + (minutes*60000L)+ (seconds*1000L)+ milliseconds)/86400000.){} + + /// \name Assignment operators + //@{ + timeperiod_t &operator =( const double &period){pd_=period; return *this;} + timeperiod_t &operator =( float period){pd_=period; return *this;} + timeperiod_t &operator =( long period){pd_=period; return *this;} + timeperiod_t &operator =( int period){pd_=period; return *this;} + timeperiod_t &operator =( short period){pd_=period; return *this;} + //@} + + /// Return time-period as a double (days). + operator double() const{return pd_;} + + /// \name Comparison operators. + //@{ + bool operator ==(const timeperiod_t &prd) const { return pd_ == prd.pd_; } + bool operator !=(const timeperiod_t &prd) const { return pd_ != prd.pd_; } + bool operator < (const timeperiod_t &prd) const { return pd_ < prd.pd_; } + bool operator > (const timeperiod_t &prd) const { return pd_ > prd.pd_; } + bool operator <=(const timeperiod_t &prd) const { return pd_ <= prd.pd_; } + bool operator >=(const timeperiod_t &prd) const { return pd_ >= prd.pd_; } + bool operator ==(dt_invalid_t) const { return pd_ == dt_invalid_; } + bool operator !=(dt_invalid_t) const { return pd_ != dt_invalid_; } + + // These shouldn't be needed. + template bool operator < (T prd) const { return pd_ < double(prd); } + template bool operator <= (T prd) const { return pd_ <= double(prd); } + template bool operator > (T prd) const { return pd_ > double(prd); } + template bool operator >= (T prd) const { return pd_ >= double(prd); } + template bool operator == (T prd) const { return pd_ == double(prd); } + template bool operator != (T prd) const { return pd_ != double(prd); } + + //@} + /// \name Simple math operators. + //@{ + timeperiod_t operator+(const timeperiod_t &prd) const { return pd_ + prd.pd_; } + timeperiod_t operator-(const timeperiod_t &prd) const { return pd_ - prd.pd_; } + timeperiod_t &operator+=(const timeperiod_t &prd) { pd_ += prd.pd_; return *this; } + timeperiod_t &operator-=(const timeperiod_t &prd) { pd_ -= prd.pd_; return *this; } + timeperiod_t operator-() const { return -pd_; } + //@} + + /// \name Conversion functions + //@{ + double as_days() { return pd_; } + void as_days(double prd) { pd_=prd; } + double as_hours() { return pd_*24; } + void as_hours(double prd) { pd_= prd/24; } + double as_minutes() { return pd_*24*60; } + void as_minutes(double prd) { pd_= prd/(24*60); } + double as_seconds() { return pd_*24*60*60; } + void as_seconds(double prd) { pd_= prd/(24*60*60); } + //@} + + /// Split up the time period into days/hours/minutes/seconds. + /** Backwards compatible. + * \deprecated + */ + void split( long& days, long& hours, long& minutes, long& seconds ) + { + split(&days,&hours,&minutes,&seconds); + } + /// Split up the time period into days/hours/minutes/seconds. + void split( long *days, long *hours, long *minutes, long *seconds, long *milliseconds = 0) + { + // Split into days and milliseconds. + double int_part; + long mspart = long(modf(pd_, &int_part) * 86400000); + *days = long(int_part); + // Optimise for integer. + if (mspart == 0 ) + { + *days = *hours = *minutes = *seconds = 0; + if (milliseconds!=NULL) *milliseconds =0; + return; + } + // Split up parts. + long ms, quot, quot2; + COMET_DIVMOD_(quot, ms, mspart, 1000); + COMET_DIVMOD_(quot2, *seconds, quot, 60); + COMET_DIVMOD_( *hours, *minutes, quot2, 60); + if( milliseconds != NULL) + *milliseconds = ms; + } + + /// Set as days/hours/minutes/seconds. + void set_period( long days, long hours, long minutes, long seconds, long milliseconds=0 ) + { + pd_ = days + ((hours*3600000L) + (minutes*60000L)+ (seconds*1000L)+ milliseconds)/86400000.; + } + + /// Return true if the period is invalid. + bool invalid() const + { + return pd_ == (double)(dt_invalid_); + } + /// Return true if the period is not invalid. + bool good() const + { + return !invalid(); + } + /** return true if the period is valid. + * \deprecated + */ + bool valid() const { return !invalid(); } + + /// Return an invalid period. + static timeperiod_t invalid_period() { return timeperiod_t( (double)(dt_invalid_)); } + +}; + + +/// A wrapper for choosing strftime/wcsftime based on char type. +template< typename CHAR > +inline size_t str_formattime( CHAR *strDest, size_t maxsize, const CHAR *format, const struct tm *timeptr ) +{ + return -1; +} +/// @if Internal +template<> +inline size_t str_formattime( char *strDest, size_t maxsize, const char *format, const struct tm *timeptr ) +{ + return strftime( strDest, maxsize, format, timeptr ); +} + +template<> +inline size_t str_formattime( wchar_t *strDest, size_t maxsize, const wchar_t *format, const struct tm *timeptr ) +{ + return wcsftime( strDest, maxsize, format, timeptr ); +} + +namespace impl { +// Internally used to group div/mod so optimiser is likely to pick it up. + + const double half_millisecond = 1.0/172800000.0; + + template + struct datetime_base + { + T dt_; + + enum convert_mode { + cmBoth, + cmOnlyTime, + cmOnlyDate + }; + + + /*! Convert absolute date to date-parts. + */ + static bool date_from_absdate_( long daysAbsolute, int *tm_year, int *tm_mon, int *tm_mday ); + + /*! Convert absolute date to day-of-week. + */ + static bool dow_from_absdate_( long daysAbsolute, int *tm_wday) + { + // Calculate the day of week (sun=0, mon=1...) + // -1 because 1/1/0 is Sat. + *tm_wday = (int)((daysAbsolute + 1) % 7L); + return true; + } + + /*! Convert date parts to absolute date. + */ + static bool absdate_from_date_( long *daysAbsolute, int tm_year, int tm_mon, int tm_mday); + + /*! Convert time in milliseconds to time of day parts. + */ + static bool time_from_milliseconds_( long milliseconds, int *tm_hour, int *tm_min, int *tm_sec, int *tm_ms); + /*! Convert time-of-day parts to milliseconds. + */ + static bool milliseconds_from_time_( long *milliseconds, unsigned short tm_hour, unsigned short tm_min, unsigned short tm_sec, unsigned short tm_ms); + + /*! Convert ole date to datetime-parts. + */ + static bool datetime_from_oledate_( DATE date, int *tm_year, int *tm_mon, int *tm_mday, int *tm_dow, int *tm_hour, int *tm_min, int *tm_sec, int *tm_ms, convert_mode mode); + + /*! Get date part of oledate. + */ + static inline long split_oledate_as_absdate_( DATE date ) + { + double val = to_double(date)+ (693959 + 1721060) + half_millisecond; // Add days from 1/1/0 to 12/30/1899 + return long(floor(val)); + } + /*! Split oledate into date/milliseconds. + */ + static inline long split_oledate_as_absdate_( DATE date, long *ms_part,bool need_ms_part ) + { + double val = to_double(date)+ (693959 + 1721060) + half_millisecond; // Add days from 1/1/0 to 12/30/1899 + if (!need_ms_part) return long(floor(val)); + *ms_part = long(modf(val, &val) * 86400000); + return long(val); + } + + /*! Join oledate. + */ + static inline DATE join_absdate_as_oledate_( long absDate, long ms_part) + { + return to_date( (double(absDate) + (ms_part / 86400000.)) - 693959 - 1721060 ); + } + + + /*! Convert datetime-parts to ole date. + */ + static bool oledate_from_datetime_( DATE *date, unsigned short tm_year, unsigned short tm_mon, unsigned short tm_mday, unsigned short tm_hour, unsigned short tm_min, unsigned short tm_sec, unsigned short tm_ms, convert_mode mode); + + /*! Convert TM to OLE date. + * Sets the date to invalid if unsuccessful. + * \retval true Successful conversion. + */ + static bool from_tm_( const struct tm &src, DATE *dt, convert_mode mode); + + /*! Convert OLE date to TM. + * \retval true Successful conversion. + */ + static bool to_tm_( DATE dt, struct tm *dest, int *ms); + + void set_invalid_() { dt_ = ((T) dt_invalid_); } + void set_null_() { dt_ = ((T) dt_null_); } + + /// Convert offset from 0 to DATE type. + static DATE to_date( double dbl) + { + if(dbl>0) return dbl; + double t=floor(dbl); + return t+(t-dbl); + } + /// Convert DATE type to offset from 0. + static double to_double( DATE dt) + { + if(dt>=0) return dt; + double t = ceil(dt); + return t-(dt-t); + } + + /// Set to value \a dt and check the range. + bool set_check_range_( T dt) + { + bool result = (dt <= dt_max && dt >= dt_min); + if (result) + dt_ = dt; + return result; + } + /// Set the value to \a dt and set 'invalid' if out of range. + void set_invalid_check_range_(T dt) + { + if (!set_check_range_(dt) ) + set_invalid_(); + } + + /// Return true if \a year is a leap-year. + static bool is_leap_year( long year) + { + if ((year & 0x3) != 0) return false; + // && ((year % 100) != 0 || (year % 400) == 0); + long quot,rem; + COMET_DIVMOD_(quot,rem, year, 100); + if (rem != 0) return true; + return ((quot & 0x3) == 0); + } + + enum { + dt_max = 2958465L, // about year 9999 + dt_null_ = 2147483648L, + dt_invalid_ = 2147483647L, + dt_min = (-657434L) // about year 100 + }; + + + static int days_in_month(int year, int month) + { + switch (month) + { + case 2: return (is_leap_year(year)?29:28); + case 4: case 6: case 9: case 11: + return 30; + default:return 31; + }; + } + + }; + //! @endif + + + // Convert TM to OLE date + template + bool + datetime_base::from_tm_( const struct tm &src, DATE *dt, convert_mode mode) + { + return oledate_from_datetime_( dt, unsigned short(src.tm_year + 1900),unsigned short( src.tm_mon+1),unsigned short( src.tm_mday),unsigned short( src.tm_hour),unsigned short( src.tm_min),unsigned short( src.tm_sec), 0U, mode); + } + + // Convert OLE date to TM. \retval true Successful conversion. + template + bool + datetime_base::to_tm_( DATE dt, struct tm *dest, int *ms) + { + int y,m,d; + if ( !datetime_from_oledate_( dt, &y, &m, &d, &dest->tm_wday, &dest->tm_hour, &dest->tm_min, &dest->tm_sec, NULL, cmBoth) ) + return false; + dest->tm_year = y; + dest->tm_mon = m; + dest->tm_mday = d; + + + if (dest->tm_year != 0) + { + long firstday, thisday; + absdate_from_date_( &thisday, y,m,d); + absdate_from_date_(&firstday, y, 1, 1); + dest->tm_yday = 1+ ( thisday - firstday); + // Convert afx internal tm to format expected by runtimes (_tcsftime, etc) + dest->tm_year -= 1900; // year is based on 1900 + dest->tm_mon -= 1; // month of year is 0-based + dest->tm_isdst = -1; // Don't know DST status. + } + else + dest->tm_yday = 0; + return true; + } + + // Convert OLE date to date-parts. + template + bool + datetime_base::date_from_absdate_(long daysAbsolute , int *tm_year, int *tm_mon, int *tm_mday) + { + // These algorithms are taken from the gregorian_calendar + // calculations in boost. + typedef long date_int_type; + typedef int year_type; + date_int_type dayNumber = daysAbsolute; + date_int_type a = dayNumber + 32044 ; + date_int_type b = (4*a + 3)/146097; + date_int_type c = a-((146097*b)/4); + date_int_type d = (4*c + 3)/1461; + date_int_type e = c - (1461*d)/4; + date_int_type m = (5*e + 2)/153; + *tm_mday = static_cast(e - ((153*m + 2)/5) + 1); + *tm_mon = static_cast(m + 3 - 12 * (m/10)); + *tm_year = static_cast(100*b + d - 4800 + (m/10)); + return true; + } + + // Convert date parts to absolute date. + template + bool + datetime_base::absdate_from_date_( long *daysAbsolute, int tm_year, int tm_month, int tm_mday) + { + // These algorithms are taken from the gregorian_calendar + // calculations in boost. + unsigned short a = static_cast((14-tm_month)/12); + unsigned short y = static_cast(tm_year + 4800 - a); + unsigned short m = static_cast(tm_month + 12*a - 3); + unsigned long d = tm_mday + ((153*m + 2)/5) + 365*y + (y/4) - (y/100) + (y/400) - 32045; + + *daysAbsolute = d; + + return true; + } + + // Convert OLE time to time of day parts. + template + bool + datetime_base::time_from_milliseconds_( long milliseconds, int *tm_hour, int *tm_min, int *tm_sec, int *tm_ms) + { + if (milliseconds == 0 ) + { + *tm_hour = *tm_min = *tm_sec = 0; + if (tm_ms!=NULL) *tm_ms =0; + return true; + } + long ms, quot, quot2; + COMET_DIVMOD_(quot, ms, milliseconds, 1000); + COMET_DIVMOD_(quot2, *tm_sec, quot, 60); + COMET_DIVMOD_( *tm_hour, *tm_min, quot2, 60); + if( tm_ms != NULL) + *tm_ms = ms; + return true; + } + + // Convert time-of-day parts to milliseconds. + template + bool + datetime_base::milliseconds_from_time_( long *milliseconds, unsigned short tm_hour, unsigned short tm_min, unsigned short tm_sec, unsigned short tm_ms) + { + if ( tm_hour > 23 || tm_min > 59 || tm_sec> 59) return false; + + *milliseconds = (tm_hour* 3600000L) + (tm_min*60000L)+ (tm_sec*1000)+ tm_ms; + return true; + } + + // + template + bool + datetime_base::datetime_from_oledate_( DATE date, int *tm_year, int *tm_mon, int *tm_mday, int *tm_wday, int *tm_hour, int *tm_min, int *tm_sec, int *tm_ms, convert_mode mode) + { + long datePart, msPart; + datePart = split_oledate_as_absdate_(date, &msPart, mode != cmOnlyDate); + if ( mode != cmOnlyDate && !time_from_milliseconds_( msPart, tm_hour, tm_min, tm_sec, tm_ms)) + return false; + return (mode == cmOnlyTime) || (date_from_absdate_( datePart, tm_year, tm_mon, tm_mday)) && ( (tm_wday==NULL) || dow_from_absdate_(datePart, tm_wday)); + } + + // Convert datetime-parts to ole date. + template + bool + datetime_base::oledate_from_datetime_( DATE *date, unsigned short tm_year, unsigned short tm_mon, unsigned short tm_mday, unsigned short tm_hour, unsigned short tm_min, unsigned short tm_sec, unsigned short tm_ms, convert_mode mode) + { + long datePart = 0, timePart = 0; + if (mode != cmOnlyDate && !milliseconds_from_time_( &timePart, tm_hour, tm_min, tm_sec, tm_ms)) + return false; + if (mode != cmOnlyTime && !absdate_from_date_( &datePart, tm_year, tm_mon, tm_mday)) + return false; + *date = join_absdate_as_oledate_(datePart, timePart); + return true; + } +} + + +/** \class datetime_t datetime.h comet\datetime.h + * Wrapper for DATE. + * DATE/TIME Represented as days + fraction of days. + */ +class datetime_t : private impl::datetime_base +{ +public: + + /// UTC/Local conversion mode. + struct utc_convert_mode + { + enum type + { + none, ///< No conversion. + local_to_utc, ///< Convert from local to utc. + utc_to_local ///< Convert from utc to local. + }; + }; + + /// Describe how to get the timezone bias. + struct timezone_bias_mode + { + enum type + { + standard, ///< Standard timezone offset + daylight_saving ///< Summer timezone offset + }; + }; + + /// Root which a time uses as an offset. + struct locality + { + enum type + { + utc, ///< A local timezone date/time. + local ///< A UTC date/time. + }; + }; + + /** \name Constructors. + * Attach to various system date/time types. + */ + //@{ + /// Constructor + datetime_t() { dt_ = 0.;} + + /// Constructor from raw DATE type. + explicit datetime_t(DATE date) + { + dt_ = date; + } + + //! Construct from date/time components. + /** If conversion fails, an valid() will return false. + */ + explicit datetime_t(int year, int month, int day, int hours=-1, int minutes=0, int seconds=0, int milliseconds=0) + { + if (!oledate_from_datetime_( &dt_, (unsigned short)year, (unsigned short)month, + (unsigned short)day, (unsigned short)hours, (unsigned short)minutes, (unsigned short)seconds, (unsigned short) milliseconds, + (hours < 0)?cmOnlyDate:cmBoth )) + set_invalid_(); + } + + /// Initialise as invalid. + datetime_t( dt_invalid_t) { dt_ = dt_invalid_; } + /// Initialise as null. + datetime_t( dt_null_t) { dt_ = dt_null_; } + /// Initialise as zero. + datetime_t( dt_zero_t) { dt_ = 0.; } + + /// Get a 'NULL' datetime. + static datetime_t get_null() { return datetime_t( DATE(dt_null_) ); } + /// Get a 'zero' datetime. + static datetime_t get_zero() { return datetime_t( DATE(0) ); } + + + //! Construct from a SYSTEMTIME. + /** Defaults to no conversion! + * \sa from_systemtime to_systemtime + */ + explicit datetime_t(const SYSTEMTIME& systimeSrc) + { + if (!from_systemtime(systimeSrc)) + set_invalid_(); + } + + /** + * Construct from a @e SYSTEMTIME. + * + * @param source + * @e SYSTEMTIME being converted. + * @param utc_mode + * Timezone conversion mode. + * @param conversion_time + * Optional override date to use for calculating daylight/standard time. + * @param utc_or_local + * Specify whether the override time is a UTC or local date. + */ + explicit datetime_t( + const SYSTEMTIME& source, utc_convert_mode::type utc_mode, + const datetime_t& conversion_time=datetime_t(dt_invalid), + locality::type utc_or_local=locality::local) + { + if (!from_systemtime(source, utc_mode, conversion_time, utc_or_local)) + set_invalid_(); + } + + /** + * Construct from a @e SYSTEMTIME. + * + * @param source + * @e SYSTEMTIME being converted. + * @param utc_mode + * Timezone conversion mode. + * @param bias_mode + * Specify whether the local time is daylight/standard time. + */ + explicit datetime_t( + const SYSTEMTIME& source, utc_convert_mode::type utc_mode, + timezone_bias_mode::type bias_mode) + { + if (!from_systemtime(source, utc_mode, bias_mode)) + set_invalid_(); + } + + /** + * Construct from a @e FILETIME. + * + * Defaults to no timezone conversion. FILETIME values are a tricky beast. + * FILETIMEs on FAT are local, as are ZIP files (mostly). On shares and + * NTFS, they are UTC. + * + * @param source + * @e FILETIME being converted. + * @param utc_mode + * Timezone conversion mode. + * @param conversion_time + * Optional override date to use for calculating daylight/standard time. + * @param utc_or_local + * Specify whether the override time is a UTC or local date. + * + * @sa from_filetime to_filetime + */ + explicit datetime_t( + const FILETIME& source, + utc_convert_mode::type utc_mode=utc_convert_mode::none, + const datetime_t& conversion_time=datetime_t(dt_invalid), + locality::type utc_or_local=locality::local) + { + if (!from_filetime(source, utc_mode, conversion_time, utc_or_local)) + set_invalid_(); + } + + /** + * Construct from a @e FILETIME. + * + * Defaults to no timezone conversion. FILETIME values are a tricky beast. + * FILETIMEs on FAT are local, as are ZIP files (mostly). On shares and + * NTFS, they are UTC. + * + * @param source + * @e FILETIME being converted. + * @param utc_mode + * Timezone conversion mode. + * @param bias_mode + * Specify whether the local time is daylight/standard time. + * + * @sa from_filetime to_filetime + */ + explicit datetime_t( + const FILETIME& source, utc_convert_mode::type utc_mode, + timezone_bias_mode::type bias_mode) + { + if (!from_filetime(source, utc_mode, bias_mode)) + set_invalid_(); + } + + /** + * Construct from a Unix time. + * + * Defaults to conversion from utc to local time as time_t is in UTC. + * + * @param source + * Unix time being converted. + * @param utc_mode + * Timezone conversion mode. + * @param conversion_time + * Optional override date to use for calculating daylight/standard time. + * @param utc_or_local + * Specify whether the override time is a UTC or local date. + * + * @sa from_unixtime to_unixtime + */ + explicit datetime_t( + time_t source, + utc_convert_mode::type utc_mode=utc_convert_mode::utc_to_local, + const datetime_t& conversion_time=datetime_t(dt_invalid), + locality::type utc_or_local=locality::local) + { + if (!from_unixtime(source, utc_mode, conversion_time, utc_or_local)) + set_invalid_(); + } + + /** + * Construct from a Unix time. + * + * Defaults to conversion from utc to local time as time_t is in UTC. + * + * @param source + * Unix time being converted. + * @param utc_mode + * Timezone conversion mode. + * @param bias_mode + * Specify whether the local time is daylight/standard time. + * + * @sa from_unixtime to_unixtime + */ + explicit datetime_t( + time_t source, utc_convert_mode::type utc_mode, + timezone_bias_mode::type bias_mode) + { + if (!from_unixtime(source, utc_mode, bias_mode)) + set_invalid_(); + } + + //! Copy constructor. + datetime_t(const datetime_t& date) + { + dt_ = date.dt_; + } + + //@} + + /// Create a const reference. + static const datetime_t& create_const_reference(const DATE& s) throw() + { return *reinterpret_cast(&s); } + /// Create a non-const reference. + static datetime_t& create_reference(DATE& s) throw() + { return *reinterpret_cast(&s); } + + /// Day-of-week enumeration. + enum day_of_week { + dow_sun=0, dow_mon, dow_tue, dow_wed, dow_thu, dow_fri, dow_sat + }; + + /// Return the current time. + static datetime_t now() + { + SYSTEMTIME lt; + ::GetLocalTime(<); + return datetime_t(lt); + } + /// Return the current utc time. + static datetime_t now_utc() + { + SYSTEMTIME lt; + ::GetSystemTime(<); + return datetime_t(lt); + } + + /** Add specified number of months. + * If the day is not valid (ie 1 month from 31 December) + * an exception will be thrown. + * \todo Add an enum to be more smart about this. + */ + datetime_t &add_months(int inc_months) + { + int year,month,day; + + split_date(&year,&month,&day); + long months = (month-1)+(year*12)+inc_months; + + long quot,rem; + COMET_DIVMOD_(quot, rem, months, 12); + if(!set_date( quot, rem+1, day)) + throw datetime_exception("Invalid Date"); + + return *this; + } + + /// Add specified number of years. + datetime_t &add_years(int inc_years) + { + int year,month,day; + split_date(&year,&month,&day); + if(!set_date( year+inc_years, month, day)) + throw datetime_exception("Invalid Date"); + return *this; + } + + /// Return year/month/day values. + void split_date(int *year, int *month, int *day) const + { + if (good()) + { + long datePart = split_oledate_as_absdate_(dt_); + if (date_from_absdate_( datePart, year, month, day) ) + return; + } + throw datetime_exception("Invalid Date"); + } + + /// Return hours/minutes/second values. + void split_time( int *hours, int *minutes, int *seconds, int *milliseconds=NULL) const + { + if(!good() || !datetime_from_oledate_(dt_, NULL, NULL, NULL, NULL, hours, minutes, seconds, milliseconds, cmOnlyTime)) + throw datetime_exception("Invalid DateTime"); + } + /// Return date/time split up. + void split(int *year, int *month, int *day, int *hours, int *minutes, int *seconds, int *milliseconds=NULL) + { + if(!good() || !datetime_from_oledate_(dt_, year, month, day, NULL, hours, minutes, seconds, milliseconds, cmBoth)) + throw datetime_exception("Invalid DateTime"); + } + + + /// \name Access date/time parts. + //@{ + /// Year. + int year() const + { + int year,month,day; + split_date(&year,&month,&day); + return year; + } + /// Month of year (1-based) + int month() const + { + int year,month,day; + split_date(&year,&month,&day); + return month; + } + /// Day of month (1-based) + int day() const + { + int year,month,day; + split_date(&year,&month,&day); + return day; + } + /// Hour part of time (0-based) ??? + int hour() const + { + int hours,minutes,seconds; + split_time(&hours,&minutes,&seconds); + return hours; + } + /// Minute part of time (0-based) + int minute() const + { + int hours,minutes,seconds; + split_time(&hours,&minutes,&seconds); + return minutes; + } + /// Second + int second() const + { + int hours,minutes,seconds; + split_time(&hours,&minutes,&seconds); + return seconds; + } + /// Milliseconds + int millisecond() const + { + int hours,minutes,seconds,ms; + split_time(&hours,&minutes,&seconds,&ms); + return ms; + } + + /// The day of week. + day_of_week dow() const + { + long datePart; + datePart = split_oledate_as_absdate_(dt_); + int wday; + if(!good() || !dow_from_absdate_(datePart, &wday)) + throw datetime_exception("Invalid Date"); + return day_of_week(wday); + } + /// Day of the year (0 -based) + int year_day() const + { + if (good()) + { + long datepart = split_oledate_as_absdate_(dt_); + int y,m,d; + date_from_absdate_(datepart, &y,&m,&d); + long firstday; + if ( absdate_from_date_(&firstday, y, 1, 1)) + return 1+ ( datepart - firstday); + } + throw datetime_exception("Invalid Date"); + } + /// Days in the month; + int days_in_month() + { + int year,month,day; + split_date(&year,&month,&day); + return impl::datetime_base::days_in_month(year,month); + } + //@} + static inline int days_in_month(int year, int month) + { + return impl::datetime_base::days_in_month(year,month); + } + + /// \name Assignment operators + //@{ + + datetime_t &operator=( const datetime_t& date) + { + dt_ = date.dt_; + return *this; + } + + datetime_t &operator=( DATE date ) + { + set_invalid_check_range_(date); + return *this; + } + //@} + + ///\name Comparison operators + //@{ + bool operator==(const datetime_t& date) const{ return date.dt_==dt_; } + bool operator!=(const datetime_t& date) const{ return date.dt_!=dt_; } + bool operator<(const datetime_t& date) const { return to_double(dt_)(const datetime_t& date) const{ return to_double(dt_)>to_double(date.dt_); } + bool operator<=(const datetime_t& date) const{ return to_double(dt_)<=to_double(date.dt_); } + bool operator>=(const datetime_t& date) const{ return to_double(dt_)>=to_double(date.dt_); } + bool operator==(dt_invalid_t) const { return invalid(); } + bool operator!=(dt_invalid_t) const { return !invalid(); } + bool operator==(dt_zero_t) const { return dt_==0.; } + bool operator!=(dt_zero_t) const { return dt_!=0.; } + bool operator==(dt_null_t) const { return null(); } + bool operator!=(dt_null_t) const { return !null(); } + //@} + + ///\name Arithmetic operators + //@{ + datetime_t operator+(const timeperiod_t& dateSpan) const + { + datetime_t dt(*this); + dt+=dateSpan; + return dt; + } + datetime_t operator-(const timeperiod_t& dateSpan) const + { + datetime_t dt(*this); + dt-=dateSpan; + return dt; + } + datetime_t& operator+=(const timeperiod_t &dateSpan) + { + COMET_ASSERT( good() ); + if(!good()) + set_invalid_(); + else + set_invalid_check_range_(to_date( to_double(dt_) + (double)dateSpan )); + + return *this; + } + datetime_t& operator-=(const timeperiod_t &dateSpan) + { + COMET_ASSERT( good() ); + if(!good()) + set_invalid_(); + else + set_invalid_check_range_(to_date( to_double(dt_) - (double)dateSpan )); + return *this; + } + timeperiod_t operator-(const datetime_t& date) const + { + COMET_ASSERT( good() && date.good() ); + if( !good() || ! date.good()) + return timeperiod_t::invalid_period(); + return to_double(dt_) - to_double(date.dt_); + } + datetime_t &operator++() + { + (*this)+=1; + return *this; + } + datetime_t operator++(int) + { + datetime_t t(*this); (*this)+=1; return t; + } + datetime_t &operator--() + { + (*this)-=1; return *this; + } + + datetime_t operator--(int) + { + datetime_t t(*this); (*this)-=1; return t; + } + //@} + + /// return true if the date is marked 'invalid'. + inline bool invalid() const { return dt_ == ((double) dt_invalid_); } + /// return true if the date is marked 'null' + inline bool null() const { return dt_ == ((double) dt_null_); } + + /// return true if date is zero + inline bool zero() const { return dt_ == 0; } + + /** return true if the date is not marked 'invalid'. + * \deprecated + */ + inline bool valid() const { return !invalid(); } + + /// Return true if the date is usable. + inline bool good() const + { + switch ((long)dt_) + { + case dt_invalid_: case dt_null_: return false; + default: return true; + } + } + + ///\name Accessor methods + //@{ + DATE get() const { if(invalid()) throw("Invalid Date"); return null()?0:dt_;} + DATE in() const { return get(); } + DATE *in_ptr() const { return const_cast(&dt_);} + DATE *out() { return &dt_;} + DATE *inout() { return &dt_;} + //@} + /** Set date part as year/month/day. + * \param year Year (from year 0 - as in 2000). + * \param month Month of year (1-based). + * \param day Day of month (1-based). + * \retval true Successfully set date. + * \retval false Conversion unsuccessful - date not set. + */ + bool set_date( int year, int month, int day) + { + long datePart, timePart; + datePart = split_oledate_as_absdate_(dt_, &timePart, true); + if (!absdate_from_date_(&datePart, year,month, day)) + return false; + dt_ = join_absdate_as_oledate_( datePart, timePart); + return true; + } + + /** Set time part as hours/minutes/seconds. + * \param hours As in a 24-hour clock. + * \param minutes Minutes past the hour. + * \param seconds Seconds past the minute. + * \param milliseconds Milliseconds past the second. + * \retval true Successfully set time. + * \retval false Conversion unsuccessful - time not set. + */ + bool set_time( int hours, int minutes, int seconds, int milliseconds =0) + { + long datePart, timePart; + datePart = split_oledate_as_absdate_(dt_, &timePart, true); + if (!milliseconds_from_time_(&timePart, (unsigned short)hours, (unsigned short)minutes, (unsigned short)seconds, (unsigned short)milliseconds)) + return false; + dt_ = join_absdate_as_oledate_( datePart, timePart); + return true; + } + /** Set both date and time. + * \param year Year (from year 0 - as in 2000). + * \param month Month of year (1-based). + * \param day Day of month (1-based). + * \param hours As in a 24-hour clock. + * \param minutes Minutes past the hour. + * \param seconds Seconds past the minute. + * \param milliseconds Milliseconds past the second. + * \retval true Successfully set date/time. + * \retval false Conversion unsuccessful - date/time not set. + */ + bool set_date_time(int year, int month, int day, int hours, int minutes, int seconds, int milliseconds = 0 ) + { + return oledate_from_datetime_(&dt_, (unsigned short)year, (unsigned short)month, (unsigned short)day, (unsigned short)hours, (unsigned short)minutes, (unsigned short)seconds, (unsigned short)milliseconds, cmBoth); + } + + /// Flags for formatting. + enum format_flags{ + ff_default = 0, ///< Default formatting. + ff_system_locale = LOCALE_NOUSEROVERRIDE, ///< Use system locale + ff_hijri = VAR_CALENDAR_HIJRI, ///< Use HIJRI calendar. + ff_thai = 0x10, /* VAR_CALENDAR_THAI, */ ///< Use thai calendar. + ff_gregorian = 0x20, /*VAR_CALENDAR_GREGORIAN*/ ///< Use gregorian calendar. + ff_four_digits = VAR_FOURDIGITYEARS, ///< Four digits for years + ff_time_only = VAR_TIMEVALUEONLY, ///< Only output time. + ff_date_only = VAR_DATEVALUEONLY ///< Only output date. + }; + /** Parse bstring to a datetime_t. + * \param val String to parse. + * \param flags valid format_flags are: ff_default, ff_system_locale, ff_hijri, ff_time_only, ff_date_only + * \param locale Locale to use. Default \p locale is the user default. + */ + datetime_t &parse( const bstr_t &val, format_flags flags = ff_default, LCID locale = LOCALE_USER_DEFAULT) + { + VarDateFromStr( val.in(), locale, flags, &dt_) | raise_exception; + return *this; + } + + /** Return a double that is sortable / can be subtracted. + * Dates before 12/30/1899 will not sort propperly. + */ + double as_sortable_double() const { COMET_ASSERT( good() ); return to_double(dt_); } + +public: + + /** + * Convert a local time to UTC. + * + * Takes a local time (like that inside a ZIP file, or on a FAT file + * system) and converts it to UTC, using the timezone rules in effect as + * of the date specified. Typically the "as of" date is specified as the + * modification or creation date of the ZIP file, or left missing to + * default to the given local date. It is also possible to specify if the + * "as of" date is in UTC or not. If missing, it defaults to false. + */ + + /** + * Create UTC version of this local time. + * + * Assuming this datetime is a local time (like that inside a ZIP file or + * on a FAT file system) this creates a new version of it as a UTC datetime. + * By default, the adjustment is made based on the timezone rules that + * would have been in effect at the UTC date this object represents. + * However, the time can also be converted as though it were on another + * date by passing another date as an argument. + * + * Typically the "as of" date is specified as the current time or possibly + * the modification or creation date of an enclosing ZIP file. + * + * @param as_of_date + * Optional alternative date on which to base the timezone conversion. + * @param utc_or_local + * Whether `as_of_date` is a local or UTC date. Defaults to UTC. + */ + datetime_t local_to_utc( + datetime_t as_of_date=datetime_t(dt_invalid), + locality::type utc_or_local=locality::local) const + { + // if they didn't specify an AS OF date, use the current date which + // will be local + if (as_of_date.invalid()) + { + utc_or_local = locality::local; // no break + as_of_date = *this; + } + + double timezone_bias = + local_timezone_bias(as_of_date, utc_or_local) / (24.*60.); + double local_date_continuous = to_double(dt_); + DATE utc_date = to_date(local_date_continuous + timezone_bias); + + return datetime_t(utc_date); + } + + /** + * Create a UTC version of this local time, explicitly using standard + * time or daylight savings. + * + * Assuming this datetime is a local time (like that inside a ZIP file or + * on a FAT file system) this creates a new version of it as a UTC datetime. + * Depending on the argument passed, the adjustment is made as though + * daylight savings were in operation in the local timezone or not. + * + * @param bias_mode + * Whether to assume daylight savings is in effect. + * + * - standard: create local time as though it were not + * daylight savings time + * - daylight_savings: create local time as though it were daylight + * savings time + */ + datetime_t local_to_utc(timezone_bias_mode::type bias_mode) const + { + double timezone_bias = local_timezone_bias(bias_mode) / (24.*60.); + double local_date_continuous = to_double(dt_); + DATE utc_date = to_date(local_date_continuous + timezone_bias); + + return datetime_t(utc_date); + } + + /** + * Create local time version of this UTC time. + * + * Assuming this datetime is a UTC time (like that on an NTFS file system) + * this creates a new version of it in the local timezone. By default, + * the adjustment is made based on the timezone rules that would have been + * in effect at the UTC date this object represents. However, the time can + * also be converted as though it were on another date by passing another + * date as an argument. + * + * Typically the "as of" date is specified as the current time or possibly + * the modification or creation date of an enclosing ZIP file. + * + * @param as_of_date + * Optional alternative date on which to base the timezone conversion. + * @param utc_or_local + * Whether `as_of_date` is a local or UTC date. Defaults to UTC. + */ + datetime_t utc_to_local( + datetime_t as_of_date=datetime_t(dt_invalid), + locality::type utc_or_local=locality::utc) const + { + // if they didn't specify an AS OF date, use the current date which + // will be UTC + if (as_of_date.invalid()) + { + as_of_date = *this; + utc_or_local = locality::utc; + } + + long timezone_bias = local_timezone_bias(as_of_date, utc_or_local); + double timezone_bias_days = timezone_bias / (24.*60.); + double utc_date_continuous = to_double(dt_); + DATE local_date = to_date(utc_date_continuous - timezone_bias_days); + + return datetime_t(local_date); + } + + /** + * Create local time version of this UTC time, explicitly using standard + * time or daylight savings. + * + * Assuming this datetime is a UTC time (like that on an NTFS file system) + * this creates a new version of it in the local timezone. Depending on + * the argument passed, the adjustment is made as though daylight savings + * were in operation in the timezone or not. + * + * @param bias_mode + * Whether to assume daylight savings. + * + * - standard: create local time as though it were not + * daylight savings time + * - daylight_savings: create local time as though it were daylight + * savings time + */ + datetime_t utc_to_local(timezone_bias_mode::type bias_mode) const + { + long timezone_bias = local_timezone_bias(bias_mode); + double timezone_bias_days = timezone_bias / (24.*60.); + double utc_date_continuous = to_double(dt_); + DATE local_date = to_date(utc_date_continuous - timezone_bias_days); + + return datetime_t(local_date); + } + + /** Convert to SYSTEMTIME struct. + */ + bool to_systemtime( SYSTEMTIME *sysTime) const + { + int year,month,day,dow,hour,minute,second,ms; + if (!datetime_from_oledate_( dt_, &year, &month, &day, &dow, &hour, &minute, &second, &ms, cmBoth)) + return false; + sysTime->wYear = (short)year; + sysTime->wMonth = (short)month; + sysTime->wDay = (short)day; + sysTime->wDayOfWeek = (short)dow; // Sunday=0 + sysTime->wHour = (short)hour; + sysTime->wMinute = (short)minute; + sysTime->wSecond = (short)second; + sysTime->wMilliseconds = (short)ms; + return true; + } + + /** Convert from a \e SYSTEMTIME struct. + */ + bool from_systemtime(const SYSTEMTIME& src) + { + return oledate_from_datetime_( &dt_, src.wYear, src.wMonth, src.wDay, src.wHour, src.wMinute, src.wSecond, src.wMilliseconds, cmBoth); + } + + /** + * Convert from a @e SYSTEMTIME struct. + * + * @param source + * @e SYSTEMTIME being converted. + * @param utc_mode + * Timezone conversion mode. + * @param conversion_time + * Optional override date to use for calculating daylight/standard time. + * @param utc_or_local + * Specify whether the override time is a UTC or local date. + */ + bool from_systemtime( + const SYSTEMTIME& source, utc_convert_mode::type utc_mode, + const datetime_t& conversion_time=datetime_t(dt_invalid), + locality::type utc_or_local=locality::local) + { + if (!from_systemtime(source)) + return false; + + switch(utc_mode) + { + case utc_convert_mode::none: + break; + case utc_convert_mode::local_to_utc: + *this = local_to_utc(conversion_time, utc_or_local); + break; + case utc_convert_mode::utc_to_local: + *this = utc_to_local(conversion_time, utc_or_local); + break; + } + return true; + } + + /** + * Convert from a @e SYSTEMTIME struct. + * + * @param source + * @e SYSTEMTIME being converted. + * @param utc_mode + * Timezone conversion mode. + * @param bias_mode + * Specify whether the local time is daylight/standard time. + */ + bool from_systemtime( + const SYSTEMTIME& source, utc_convert_mode::type utc_mode, + timezone_bias_mode::type bias_mode) + { + if (!from_systemtime(source)) + return false; + + switch(utc_mode) + { + case utc_convert_mode::none: + break; + case utc_convert_mode::local_to_utc: + *this = local_to_utc(bias_mode); + break; + case utc_convert_mode::utc_to_local: + *this = utc_to_local(bias_mode); + break; + } + return true; + } + + /** Convert from a \e FILETIME struct. + */ + bool from_filetime(const FILETIME& src) + { + double ftd = (((__int64(src.dwHighDateTime) << 32 | src.dwLowDateTime)/(36000000000.)) - 2620920.)/24; + return set_check_range_( to_date(ftd)); + } + + /** + * Convert from a @e FILETIME struct. + * + * @param source + * @e FILETIME being converted. + * @param utc_mode + * Timezone conversion mode. + * @param conversion_time + * Optional override date to use for calculating daylight/standard time. + * @param utc_or_local + * Specify whether the override time is a UTC or local date. + */ + bool from_filetime( + const FILETIME& source, utc_convert_mode::type utc_mode, + const datetime_t& conversion_time=datetime_t(dt_invalid), + locality::type utc_or_local=locality::local) + { + if (!from_filetime(source)) + return false; + + switch(utc_mode) + { + case utc_convert_mode::none: + break; + case utc_convert_mode::local_to_utc: + *this = local_to_utc(conversion_time, utc_or_local); + break; + case utc_convert_mode::utc_to_local: + *this = utc_to_local(conversion_time, utc_or_local); + break; + } + return true; + } + + /** + * Convert from a @e FILETIME struct. + * + * @param source + * @e FILETIME being converted. + * @param utc_mode + * Timezone conversion mode. + * @param bias_mode + * Specify whether the local time is daylight/standard time. + */ + bool from_filetime( + const FILETIME& source, utc_convert_mode::type utc_mode, + timezone_bias_mode::type bias_mode) + { + if (!from_filetime(source)) + return false; + + switch(utc_mode) + { + case utc_convert_mode::none: + break; + case utc_convert_mode::local_to_utc: + *this = local_to_utc(bias_mode); + break; + case utc_convert_mode::utc_to_local: + *this = utc_to_local(bias_mode); + break; + } + return true; + } + + /** Convert to a \e FILETIME struct. + */ + bool to_filetime( FILETIME *filetime) const + { + double val = ((to_double(dt_) * 24.) + 2620920.)*(36000000000.) ; + + __int64 llval = __int64(val); + filetime->dwHighDateTime = long (llval >> 32); + filetime->dwLowDateTime = long (llval); + return val > 0; + } + + /** Convert from a \e tm struct. + */ + bool from_tm(const struct tm &tm_time) + { + return from_tm_( tm_time, &dt_, cmBoth); + } + + /** + * Convert from a @e tm struct. + * @param tm_time + * @e tm struct being converted. + * @param utc_mode + * Timezone conversion mode. + * @param conversion_time + * Optional override date to use for calculating daylight/standard time. + * Only used if the information cannot be derived from the @e tm struct. + * @param utc_or_local + * Whether the optional conversion date is UTC or local. + */ + bool from_tm( + const struct tm &tm_time, utc_convert_mode::type utc_mode, + datetime_t conversion_time=datetime_t(dt_invalid), + locality::type utc_or_local=locality::local) + { + if(!from_tm(tm_time)) + return false; + + switch(utc_mode) + { + case utc_convert_mode::none: + break; + case utc_convert_mode::local_to_utc: + // Take advantage of tm_isdst to work out dst mode! + // + // XXX: This doesn't use the conversion_time at all. No, I don't + // know why we have this behaviour + if (tm_time.tm_isdst == 0) + { + *this = local_to_utc(timezone_bias_mode::standard); + } + else if (tm_time.tm_isdst > 0) + { + *this = local_to_utc(timezone_bias_mode::daylight_saving); + } + else + { + *this = local_to_utc(conversion_time, utc_or_local); + } + break; + case utc_convert_mode::utc_to_local: + *this = utc_to_local(conversion_time, utc_or_local); + break; + } + + return true; + } + + /** + * Convert from a @e tm struct. + * + * @param tm_time + * @e 'tm' struct being converted. + * @param utc_mode + * Timezone conversion mode. + * @param daylight_hint + * Strategy to use for daylight savings time if it cannot be derived + * from the @e tm struct. + */ + bool from_tm( + const struct tm &tm_time, utc_convert_mode::type utc_mode, + timezone_bias_mode::type daylight_hint) + { + if(!from_tm(tm_time)) + return false; + + switch(utc_mode) + { + case utc_convert_mode::none: + break; + case utc_convert_mode::local_to_utc: + // Take advantage of tm_isdst to work out dst mode! + // + // XXX: This overrides the specified conversion. No, I don't + // know why we have this behaviour + if (tm_time.tm_isdst == 0) + { + *this = local_to_utc(timezone_bias_mode::standard); + } + else if (tm_time.tm_isdst > 0) + { + *this = local_to_utc(timezone_bias_mode::daylight_saving); + } + else + { + *this = local_to_utc(daylight_hint); + } + break; + case utc_convert_mode::utc_to_local: + *this = utc_to_local(daylight_hint); + break; + } + + return true; + } + + + /** + * Convert from a @e time_t value. + * + * @param source + * Unix time to convert. + * @param utc_mode + * Timezone conversion mode. By default converts a UTC time_t into + * a local datetime. + * @param conversion_time + * Optional override date to use for calculating daylight/standard time. + * @param utc_or_local + * Specify whether the override time is a UTC or local date. + */ + bool from_unixtime( + time_t source, + utc_convert_mode::type utc_mode=utc_convert_mode::utc_to_local, + const datetime_t& conversion_time=datetime_t(dt_invalid), + locality::type utc_or_local=locality::local) + { + FILETIME ft; + __int64 ll = (__int64(source) * 10000000L) + 116444736000000000L; + ft.dwLowDateTime = (DWORD) ll; + ft.dwHighDateTime = (DWORD)(ll >>32); + return from_filetime(ft, utc_mode, conversion_time, utc_or_local); + } + + /** + * Convert from a @e time_t value. + * + * @param source + * Unix time being converted. + * @param utc_mode + * Timezone conversion mode. + * @param bias_mode + * Specify whether the local time is daylight/standard time. + */ + bool from_unixtime( + time_t source, utc_convert_mode::type utc_mode, + timezone_bias_mode::type bias_mode) + { + FILETIME ft; + __int64 ll = (__int64(source) * 10000000L) + 116444736000000000L; + ft.dwLowDateTime = (DWORD) ll; + ft.dwHighDateTime = (DWORD)(ll >>32); + return from_filetime(ft, utc_mode, bias_mode); + } + + /** + * Convert to a @e time_t value. + * + * @param unix_time_out + * Destination of conversion result. + * @param utc_mode + * Timezone conversion mode. + * @param conversion_time + * Optional override date to use for calculating daylight/standard time. + * @param utc_or_local + * Specify whether the override time is a UTC or local date. + */ + bool to_unixtime( + time_t* unix_time_out, + utc_convert_mode::type utc_mode=utc_convert_mode::local_to_utc, + const datetime_t& conversion_time=datetime_t(dt_invalid), + locality::type utc_or_local=locality::local) const + { + datetime_t dtval; + + switch(utc_mode) + { + case utc_convert_mode::none: + dtval = *this; + break; + case utc_convert_mode::local_to_utc: + dtval = local_to_utc(conversion_time, utc_or_local); + break; + case utc_convert_mode::utc_to_local: + dtval = utc_to_local(conversion_time, utc_or_local); + break; + } + + FILETIME ft; + if (!dtval.to_filetime(&ft)) + return false; + + *unix_time_out = + time_t(((__int64(ft.dwHighDateTime) << 32 | ft.dwLowDateTime) - + 116444736000000000L)/10000000L); + return true; + } + + /** + * Convert to a @e time_t value. + * + * @param unix_time_out + * Destination of conversion result. + * @param utc_mode + * Timezone conversion mode. + * @param conversion_time + * Optional override date to use for calculating daylight/standard time. + * @param utc_or_local + * Specify whether the override time is a UTC or local date. + */ + bool to_unixtime( + time_t* unix_time_out, utc_convert_mode::type utc_mode, + timezone_bias_mode::type bias_mode) const + { + datetime_t dtval; + + switch(utc_mode) + { + case utc_convert_mode::none: + dtval = *this; + break; + case utc_convert_mode::local_to_utc: + dtval = local_to_utc(bias_mode); + break; + case utc_convert_mode::utc_to_local: + dtval = utc_to_local(bias_mode); + break; + } + + FILETIME ft; + if (!dtval.to_filetime(&ft)) + return false; + + *unix_time_out = + time_t(((__int64(ft.dwHighDateTime) << 32 | ft.dwLowDateTime) - + 116444736000000000L)/10000000L); + return true; + } + + /** + * Calculate the local timezone's offset from UTC on the given date, in + * minutes. + * + * This offset is called the bias and is the number of minutes to subtract + * from a UTC time to make a local one. + * + * @param dt + * The date and time for which to calculate the bias. + * @param is_utc + * Whether to interpret the date as UTC or local. + * + * @todo A better way might be to make UTCness a fundamental property of + * times at construction so they know whether they are URL or local + * themselves. + */ + static long local_timezone_bias(datetime_t dt, locality::type utc_or_local) + { + TIME_ZONE_INFORMATION tzi; + ::GetTimeZoneInformation(&tzi); + + long baseBias = tzi.Bias; + + // if we've even got both time zones set, we have to choose which is + // active... + if ((tzi.DaylightDate.wMonth != 0) && (tzi.StandardDate.wMonth != 0)) + { + // all local standard time/daylight savings time rules are based on + // local-time, so add the base bias FIRST + if (utc_or_local == locality::utc) + dt -= (baseBias / (24.*60.)); + + SYSTEMTIME sysTime; + if (!dt.to_systemtime(&sysTime)) + throw datetime_exception("Invalid Date"); + + bool DSTbeforeLST = + tzi.DaylightDate.wMonth < tzi.StandardDate.wMonth; + + bool afterDaylightStarts = + tz_on_or_after_in_year(sysTime, tzi.DaylightDate); + bool afterStandardStarts = + tz_on_or_after_in_year(sysTime, tzi.StandardDate); + + if (((afterDaylightStarts== afterStandardStarts)!= DSTbeforeLST)) + return baseBias + tzi.DaylightBias; + } + + return baseBias + tzi.StandardBias; + } + + /** + * Calculate the local timezone's offset from UTC in minutes. + * + * The value depends on the argument to the function which specifies + * whether to assume daylight savings is in operation in the local timezone. * occuring outside of daylight savings time. + */ + static long local_timezone_bias(timezone_bias_mode::type dst_state) + { + TIME_ZONE_INFORMATION tzi; + ::GetTimeZoneInformation(&tzi); + + switch (dst_state) + { + case timezone_bias_mode::standard: + return tzi.Bias + tzi.StandardBias; + case timezone_bias_mode::daylight_saving: + return tzi.Bias + tzi.DaylightBias; + default: + COMET_ASSERT(!"Invalid timezone daylight savings state"); + } + } + +protected: + + /** Compares two SYSTEMTIME values to decide if the second is after (or on) the + * first. + * If the year is supplied, the two dates are assumed static, otherwise it + * computes the proper day-of-week instance (like last Sunday in October) for + * the specified test year. See the encoding rules documented with + * TIME_ZONE_INFORMATION + */ + static bool tz_on_or_after_in_year(SYSTEMTIME testST, SYSTEMTIME tziST) + { + // assume month check first... + long cmp = testST.wMonth - tziST.wMonth; + if (cmp!=0) + return cmp > 0; + + SYSTEMTIME absST; + + // if year is given, then the specified date is already exact... + if (tziST.wYear != 0) + { + // first test the year... + cmp = testST.wYear - tziST.wYear; + if (cmp !=0) + return cmp > 0; + // carry on with the exact day known + absST = tziST; + } + else + { + // compute the appropriate day from the specified instance of the set day-of-week + // use the testST's year for the year in the calculation + tz_convert_relative_dow_to_absolute(testST, tziST, &absST); + } + + // month same... check day/hour/minute/second/millisecond + if ((cmp = testST.wDay - absST.wDay)==0) + if ((cmp = testST.wHour - absST.wHour)==0) + if ((cmp = testST.wMinute - absST.wMinute)==0) + if ((cmp = testST.wSecond - absST.wSecond)==0) + cmp = testST.wMilliseconds - absST.wMilliseconds; + return cmp >= 0; + } + + // Computes the proper day-of-week instance (like last Sunday in October) for the + // specified test year. See the encoding rules documented with TIME_ZONE_INFORMATION. + // This ASSUMES that testST.wMonth == tziST.wMonth + static void tz_convert_relative_dow_to_absolute(const SYSTEMTIME &testST , const SYSTEMTIME &tziST, SYSTEMTIME *absST) + { + COMET_ASSERT(testST.wMonth == tziST.wMonth); + + // Set up the absolute date except for wDay, which we must find + absST->wYear = testST.wYear; // year is only valid in the testST + int month = absST->wMonth = tziST.wMonth; + absST->wDayOfWeek = tziST.wDayOfWeek; + + absST->wHour = tziST.wHour; + absST->wMinute = tziST.wMinute; + absST->wSecond = tziST.wSecond; + absST->wMilliseconds = tziST.wMilliseconds; + + // Find a day of the month that falls on the same day of the week as + // the transition. + + // If test day is the 29th of the month (testST.wDay = 29) and today + // is a Tuesday (testST.wDayOfWeek = 2) and the transition occurs on + // Sunday (testST.wDayOfWeek = 0) we compute absDay = 29 + 0 + 7 - + // 2, giving the 34th + + // then adjust that to a day of month adjustment + long absDay = ((testST.wDay + tziST.wDayOfWeek + (7-1) - testST.wDayOfWeek) % 7) +1; + + // now multiply this time the "which DOW" setting from the TZI + // (1 = first, 5 = last) + // add the requisite number of weeks to the base point + absDay += (7 * (tziST.wDay - 1)); + + // and if we exceeded the number of days in the month, back up by a + // week (this handles the 5=last situation) + + int daysInMonth = days_in_month( absST->wYear, month); + + if (absDay > daysInMonth) + absDay -= 7; + + absST->wDay = (unsigned short)absDay; + } + +public: + + /** Format datetime_t as bstr. + * \param flags Format (can be or-ed). All format_flags are valid. + * \param locale Locale ID (default to User Default. + */ + bstr_t format( format_flags flags = ff_default , LCID locale = LOCALE_USER_DEFAULT) const + { + bstr_t strDate; + if (!good()) + { + return strDate; + } + VarBstrFromDate(dt_, locale, flags, strDate.out()) | raise_exception; + return strDate; + } + + /** Format datetime_t as basic_string. + * \param fmt See system documentation for strftime for details. + */ + template + std::basic_string format( const std::basic_string &fmt ) const + { + return format(fmt.c_str()); + } + + /** Format datetime_t as basic_string. + * \param fmt See system documentation for strftime for details. + */ + template + std::basic_string format( const CHAR *fmt ) const + { + if (!good()) + { + return std::basic_string(); + } + struct tm src; + if(!to_tm_( dt_, &src, NULL)) + throw datetime_exception("Invalid Date"); + + typename auto_buffer_t::size_type capacity = 50; + auto_buffer_t buf(capacity); + size_t ret; + while( (ret = str_formattime( buf.get() , capacity, fmt, &src ))==0 && capacity < 1024) + { + capacity += 50; + buf.resize(capacity); + } + if(ret == 0) + buf.at(0)='\0'; + + return std::basic_string(buf.get(), ret); + } + + /// Detach the raw date from the class. + DATE detach() + { + DATE val = dt_; + dt_ = 0.; + return val; + } + + /// Detach the raw date from the class. + static DATE detach( datetime_t &dt) + { + return dt.detach(); + } + + /// Stream operator. + friend + std::basic_ostream &operator<<(std::basic_ostream &os, const datetime_t &val) + { + os << val.format(); + return os; + } + + /// Stream operator. + friend + std::basic_ostream &operator<<(std::basic_ostream &os, const datetime_t &val) + { + os << val.format(); + return os; + } + +private: +}; +#undef COMET_DIVMOD_ +//@} + +} +#endif diff --git a/lib/local/CamCom/comet/dispatch.h b/lib/local/CamCom/comet/dispatch.h new file mode 100644 index 00000000..c631a715 --- /dev/null +++ b/lib/local/CamCom/comet/dispatch.h @@ -0,0 +1,675 @@ +/** \file + * Provides dispatch driver via wrap_t< ::IDispatch> + * Provides dynamic implementation of IDispatch via dynamic_dispatch. + */ +/* + * Copyright 2001 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_DISPATCH_H +#define COMET_DISPATCH_H + +#include + +#include + +namespace comet { + /*! \addtogroup Interfaces + */ + //@{ + + /** Specialisation of wrap_t for IDispatch. + * Implements wrappers for the call-by name and call-by dispid for IDispatch methods + * and properties. The wrapper supports properties with up to 3 arguments and methods + * with up to 4 arguments. + * \code + com_ptr disp( my_dual_interface); + variant_t val = disp->get(L"Name"); + * \endcode + * See \ref cometcomptrsmartwrapper for details on wrap_t. + */ + template<> struct wrap_t< ::IDispatch> + { + /** Get property by dispid. + */ + variant_t get(DISPID id) + { + VARIANT result; + VARIANT* vars = 0; + DISPPARAMS disp = { vars, 0, 0, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + + /** Get property by name. + */ + variant_t get(const wchar_t* name) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return get(id); + } + + /** Get property by dispid with 1 argument. + */ + variant_t get(DISPID id, const variant_t& a0) + { + VARIANT result; + VARIANT vars[1]; vars[0] = a0.in(); + DISPPARAMS disp = { vars, 0, 1, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + + /** Get property by name with 1 argument. + */ + variant_t get(const wchar_t* name, const variant_t& a0) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return get(id, a0); + } + + /** Get property by dispid with 2 arguments. + */ + variant_t get(DISPID id, const variant_t& a1, const variant_t& a0) + { + VARIANT result; + VARIANT vars[2]; vars[0] = a0.in(); vars[1] = a1.in(); + DISPPARAMS disp = { vars, 0, 2, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + + /** Get property by name with 2 arguments. + */ + variant_t get(const wchar_t* name, const variant_t& a1, const variant_t& a0) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return get(id, a1, a0); + } + + /** Get property by dispid with 3 arguments. + */ + variant_t get(DISPID id, const variant_t& a2, const variant_t& a1, const variant_t& a0) + { + VARIANT result; + VARIANT vars[3]; vars[0] = a0.in(); vars[1] = a1.in(); vars[2] = a2.in(); + DISPPARAMS disp = { vars, 0, 3, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + + /** Get property by name with 3 arguments. + */ + variant_t get(const wchar_t* name, const variant_t& a2, const variant_t& a1, const variant_t& a0) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return get(id, a2, a1, a0); + } + + /** Put property by dispid. + */ + void put(DISPID id, const variant_t& val) + { + VARIANT vars[1]; vars[0] = val.in(); + DISPID did = DISPID_PROPERTYPUT; + DISPPARAMS disp = { vars, &did, 1, 1 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &disp, 0, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + } + + /** Put property by name. + */ + void put(const wchar_t* name, const variant_t& val) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + put(id, val); + } + + /** Put property with 1 argument by dispid. + */ + void put(DISPID id, const variant_t& a1, const variant_t& val) + { + VARIANT vars[2]; vars[0] = val.in(); vars[1] = a1.in(); + DISPID did = DISPID_PROPERTYPUT; + DISPPARAMS disp = { vars, &did, 2, 1 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &disp, 0, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + } + + /** Put property with 1 argument by name. + */ + void put(const wchar_t* name, const variant_t& a1, const variant_t& val) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + put(id, a1, val); + } + + + /** Put property with 2 arguments by dispid. + */ + void put(DISPID id, const variant_t& a2, const variant_t& a1, const variant_t& val) + { + VARIANT vars[3]; vars[0] = val.in(); vars[1] = a1.in(); vars[2] = a2.in(); + DISPID did = DISPID_PROPERTYPUT; + DISPPARAMS disp = { vars, &did, 3, 1 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &disp, 0, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + } + + /** Put property with 2 arguments by name. + */ + void put(const wchar_t* name, const variant_t& a2, const variant_t& a1, const variant_t& val) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + put(id, a2, a1, val); + } + + /** Put property with 3 arguments by dispid. + */ + void put(DISPID id, const variant_t& a3, const variant_t& a2, const variant_t& a1, const variant_t& val) + { + VARIANT vars[4]; vars[0] = val.in(); vars[1] = a1.in(); vars[2] = a2.in(); vars[3] = a3.in(); + DISPID did = DISPID_PROPERTYPUT; + DISPPARAMS disp = { vars, &did, 4, 1 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &disp, 0, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + } + + /** Put property with 3 arguments by name. + */ + void put(const wchar_t* name, const variant_t& a3, const variant_t& a2, const variant_t& a1, const variant_t& val) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + put(id, a3, a2, a1, val); + } + + /** Put property by reference by dispid. + */ + void putref(DISPID id, const variant_t& val) + { + VARIANT vars[1]; vars[0] = val.in(); + DISPID did = DISPID_PROPERTYPUT; + DISPPARAMS disp = { vars, &did, 1, 1 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, &disp, 0, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + } + + /** Put property by reference by name. + */ + void putref(const wchar_t* name, const variant_t& val) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + putref(id, val); + } + + void putref(DISPID id, const variant_t& a1, const variant_t& val) + { + VARIANT vars[2]; vars[0] = val.in(); vars[1] = a1.in(); + DISPID did = DISPID_PROPERTYPUT; + DISPPARAMS disp = { vars, &did, 2, 1 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, &disp, 0, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + } + + void putref(const wchar_t* name, const variant_t& a1, const variant_t& val) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + putref(id, a1, val); + } + + void putref(DISPID id, const variant_t& a2, const variant_t& a1, const variant_t& val) + { + VARIANT vars[3]; vars[0] = val.in(); vars[1] = a1.in(); vars[2] = a2.in(); + DISPID did = DISPID_PROPERTYPUT; + DISPPARAMS disp = { vars, &did, 3, 1 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, &disp, 0, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + } + + void putref(const wchar_t* name, const variant_t& a2, const variant_t& a1, const variant_t& val) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + putref(id, a2, a1, val); + } + + void putref(DISPID id, const variant_t& a3, const variant_t& a2, const variant_t& a1, const variant_t& val) + { + VARIANT vars[4]; vars[0] = val.in(); vars[1] = a1.in(); vars[2] = a2.in(); vars[3] = a3.in(); + DISPID did = DISPID_PROPERTYPUT; + DISPPARAMS disp = { vars, &did, 4, 1 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, &disp, 0, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + } + + void putref(const wchar_t* name, const variant_t& a3, const variant_t& a2, const variant_t& a1, const variant_t& val) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + putref(id, a3, a2, a1, val); + } + + /** Call method by dispid. + */ + variant_t call(DISPID id) + { + VARIANT result; + VARIANT* vars = 0; + DISPPARAMS disp = { vars, 0, 0, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + + /** Call method by name. + */ + variant_t call(const wchar_t* name) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return call(id); + } + + variant_t call(DISPID id, const variant_t& a0) + { + VARIANT result; + VARIANT vars[1]; vars[0] = a0.in(); + DISPPARAMS disp = { vars, 0, 1, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + + variant_t call(const wchar_t* name, const variant_t& a0) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return call(id, a0); + } + + variant_t call(DISPID id, const variant_t& a1, const variant_t& a0) + { + VARIANT result; + VARIANT vars[2]; vars[0] = a0.in(); vars[1] = a1.in(); + DISPPARAMS disp = { vars, 0, 2, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + + variant_t call(const wchar_t* name, const variant_t& a1, const variant_t& a0) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return call(id, a1, a0); + } + + variant_t call(DISPID id, const variant_t& a2, const variant_t& a1, const variant_t& a0) + { + VARIANT result; + VARIANT vars[3]; vars[0] = a0.in(); vars[1] = a1.in(); vars[2] = a2.in(); + DISPPARAMS disp = { vars, 0, 3, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + + variant_t call(const wchar_t* name, const variant_t& a2, const variant_t& a1, const variant_t& a0) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return call(id, a2, a1, a0); + } + + variant_t call(DISPID id, const variant_t& a3, const variant_t& a2, const variant_t& a1, const variant_t& a0) + { + VARIANT result; + VARIANT vars[4]; vars[0] = a0.in(); vars[1] = a1.in(); vars[2] = a2.in(); vars[3] = a3.in(); + DISPPARAMS disp = { vars, 0, 4, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + + variant_t call(const wchar_t* name, const variant_t& a3, const variant_t& a2, const variant_t& a1, const variant_t& a0) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return call(id, a3, a2, a1, a0); + } + variant_t call(DISPID id, const variant_t& a4, const variant_t& a3, const variant_t& a2, const variant_t& a1, const variant_t& a0) + { + VARIANT result; + VARIANT vars[5]; vars[0] = a0.in(); vars[1] = a1.in(); vars[2] = a2.in(); vars[3] = a3.in(); vars[4] = a4.in(); + DISPPARAMS disp = { vars, 0, 5, 0 }; + HRESULT hr = raw(this)->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &result, 0, 0); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return auto_attach(result); + } + variant_t call(const wchar_t* name, const variant_t& a4, const variant_t& a3, const variant_t& a2, const variant_t& a1, const variant_t& a0) + { + DISPID id; + HRESULT hr = raw(this)->GetIDsOfNames(IID_NULL, const_cast(&name), 1, LOCALE_USER_DEFAULT, &id); + if (FAILED(hr)) throw_com_error(raw(this), hr); + return call(id, a4, a3, a2, a1, a0); + } + }; + /** \class dynamic_dispatch dispatch.h comet/dispatch.h + * Implementation of a dynamic IDispatch, allowing methods to be added to + * an IDispatch implementation. + * The class needs to be inherited from to be able to add methods. + * Each method can have up to 4 parameters. + */ + +template class ATL_NO_VTABLE dynamic_dispatch : public ::IDispatch { + + struct method_ptr { + bool has_retval; + union { + void (BASE::*pm00)(); + void (BASE::*pm01)(const variant_t&); + void (BASE::*pm02)(const variant_t&, const variant_t&); + void (BASE::*pm03)(const variant_t&, const variant_t&, const variant_t&); + void (BASE::*pm04)(const variant_t&, const variant_t&, const variant_t&, const variant_t&); + + variant_t (BASE::*pm10)(); + variant_t (BASE::*pm11)(const variant_t&); + variant_t (BASE::*pm12)(const variant_t&, const variant_t&); + variant_t (BASE::*pm13)(const variant_t&, const variant_t&, const variant_t&); + variant_t (BASE::*pm14)(const variant_t&, const variant_t&, const variant_t&, const variant_t&); + }; + }; + + typedef std::map NAMEMAP; + NAMEMAP name_map_; + + // Workaround VC internal compiler error + struct wrap_map_t { + std::map m; + }; + typedef std::map MAP; + MAP map_; + + void add_method(const wchar_t* name, method_ptr p, DISPID id, int type) + { + if (id == flag_value) + { + NAMEMAP::const_iterator it = name_map_.find(name); + if (it == name_map_.end()) + { + id = 100000 + map_.size(); + while (map_.find(id) != map_.end()) ++id; + } + else + { + id = it->second; + } + } + + name_map_[name] = id; + map_[id].m[type] = p; + } + enum { flag_value = MINLONG }; + public: + typedef ::IDispatch interface_is; + + protected: + + void remove(const wchar_t* name) + { + NAMEMAP::iterator it = name_map_.find(name); + if (it != name_map_.end()) { + DISPID id = it->second; + name_map_.erase(it); + + map_.erase(id); + } + } + + void add_method(const wchar_t* name, void (BASE::*pm)(), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm00 = pm; add_method( name, p, id, 0 << 16 | DISPATCH_METHOD ); } + + void add_method(const wchar_t* name, void (BASE::*pm)(const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm01 = pm; add_method( name, p, id, 1 << 16 | DISPATCH_METHOD ); } + + void add_method(const wchar_t* name, void (BASE::*pm)(const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm02 = pm; add_method( name, p, id, 2 << 16 | DISPATCH_METHOD ); } + + void add_method(const wchar_t* name, void (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm03 = pm; add_method( name, p, id, 3 << 16 | DISPATCH_METHOD ); } + + void add_method(const wchar_t* name, void (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm04 = pm; add_method( name, p, id, 4 << 16 | DISPATCH_METHOD ); } + + void add_method(const wchar_t* name, variant_t (BASE::*pm)(), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm10 = pm; add_method( name, p, id, 0 << 16 | DISPATCH_METHOD ); } + + void add_method(const wchar_t* name, variant_t (BASE::*pm)(const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm11 = pm; add_method( name, p, id, 1 << 16 | DISPATCH_METHOD ); } + + void add_method(const wchar_t* name, variant_t (BASE::*pm)(const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm12 = pm; add_method( name, p, id, 2 << 16 | DISPATCH_METHOD ); } + + void add_method(const wchar_t* name, variant_t (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm13 = pm; add_method( name, p, id, 3 << 16 | DISPATCH_METHOD ); } + + void add_method(const wchar_t* name, variant_t (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm14 = pm; add_method( name, p, id, 4 << 16 | DISPATCH_METHOD ); } + + void add_put_property(const wchar_t* name, void (BASE::*pm)(const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm01 = pm; add_method( name, p, id, 1 << 16 | DISPATCH_PROPERTYPUT ); } + + void add_put_property(const wchar_t* name, void (BASE::*pm)(const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm02 = pm; add_method( name, p, id, 2 << 16 | DISPATCH_PROPERTYPUT ); } + + void add_put_property(const wchar_t* name, void (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm03 = pm; add_method( name, p, id, 3 << 16 | DISPATCH_PROPERTYPUT ); } + + void add_put_property(const wchar_t* name, void (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm04 = pm; add_method( name, p, id, 4 << 16 | DISPATCH_PROPERTYPUT ); } + + void add_putref_property(const wchar_t* name, void (BASE::*pm)(const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm01 = pm; add_method( name, p, id, 1 << 16 | DISPATCH_PROPERTYPUTREF ); } + + void add_putref_property(const wchar_t* name, void (BASE::*pm)(const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm02 = pm; add_method( name, p, id, 2 << 16 | DISPATCH_PROPERTYPUTREF ); } + + void add_putref_property(const wchar_t* name, void (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm03 = pm; add_method( name, p, id, 3 << 16 | DISPATCH_PROPERTYPUTREF ); } + + void add_putref_property(const wchar_t* name, void (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = false; p.pm04 = pm; add_method( name, p, id, 4 << 16 | DISPATCH_PROPERTYPUTREF ); } + + void add_get_property(const wchar_t* name, variant_t (BASE::*pm)(), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm10 = pm; add_method( name, p, id, 0 << 16 | DISPATCH_PROPERTYGET ); } + + void add_get_property(const wchar_t* name, variant_t (BASE::*pm)(const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm11 = pm; add_method( name, p, id, 1 << 16 | DISPATCH_PROPERTYGET ); } + + void add_get_property(const wchar_t* name, variant_t (BASE::*pm)(const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm12 = pm; add_method( name, p, id, 2 << 16 | DISPATCH_PROPERTYGET ); } + + void add_get_property(const wchar_t* name, variant_t (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm13 = pm; add_method( name, p, id, 3 << 16 | DISPATCH_PROPERTYGET ); } + + void add_get_property(const wchar_t* name, variant_t (BASE::*pm)(const variant_t&, const variant_t&, const variant_t&, const variant_t&), DISPID id = flag_value) + { method_ptr p; p.has_retval = true; p.pm14 = pm; add_method( name, p, id, 4 << 16 | DISPATCH_PROPERTYGET ); } + + private: + STDMETHOD(Invoke)(DISPID id, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pd, VARIANT* pVarResult, EXCEPINFO* pe, UINT* pu) + { + unsigned type = pd->cArgs << 16 | (wFlags & 15); + typename MAP::const_iterator it2 = map_.find(id); + if (it2 == map_.end()) return DISP_E_MEMBERNOTFOUND; + + typename std::map::const_iterator it = it2->second.m.find(type); + if (it == it2->second.m.end()) return DISP_E_BADPARAMCOUNT; + + try { + + if (pd->cNamedArgs > 1) return DISP_E_NONAMEDARGS; + + if (pd->cNamedArgs == 1) { + + if ((wFlags & 15) != DISPATCH_PROPERTYPUT && (wFlags & 15) != DISPATCH_PROPERTYPUTREF) return DISP_E_NONAMEDARGS; + if (pd->rgdispidNamedArgs[0] != DISPID_PROPERTYPUT) return DISP_E_NONAMEDARGS; + + switch (pd->cArgs) + { + case 1: + (static_cast(this)->*it->second.pm01)(variant_t::create_reference(pd->rgvarg[0])); + break; + case 2: + (static_cast(this)->*it->second.pm02)(variant_t::create_reference(pd->rgvarg[1]), variant_t::create_reference(pd->rgvarg[0])); + break; + case 3: + (static_cast(this)->*it->second.pm03)(variant_t::create_reference(pd->rgvarg[2]), variant_t::create_reference(pd->rgvarg[1]), variant_t::create_reference(pd->rgvarg[0])); + break; + case 4: + (static_cast(this)->*it->second.pm04)(variant_t::create_reference(pd->rgvarg[3]), variant_t::create_reference(pd->rgvarg[2]), variant_t::create_reference(pd->rgvarg[1]), variant_t::create_reference(pd->rgvarg[0])); + break; + default: + return DISP_E_MEMBERNOTFOUND; + } + + } + else + { + + variant_t rv; + if (it->second.has_retval) + { + switch (pd->cArgs) + { + case 0: + rv = (static_cast(this)->*it->second.pm10)(); + break; + case 1: + rv = (static_cast(this)->*it->second.pm11)(variant_t::create_reference(pd->rgvarg[0])); + break; + case 2: + rv = (static_cast(this)->*it->second.pm12)(variant_t::create_reference(pd->rgvarg[1]), variant_t::create_reference(pd->rgvarg[0])); + break; + case 3: + rv = (static_cast(this)->*it->second.pm13)(variant_t::create_reference(pd->rgvarg[2]), variant_t::create_reference(pd->rgvarg[1]), variant_t::create_reference(pd->rgvarg[1])); + break; + case 4: + rv = (static_cast(this)->*it->second.pm14)(variant_t::create_reference(pd->rgvarg[3]), variant_t::create_reference(pd->rgvarg[1]), variant_t::create_reference(pd->rgvarg[2]), variant_t::create_reference(pd->rgvarg[0])); + break; + default: + return DISP_E_MEMBERNOTFOUND; + } + } + else + { + switch (pd->cArgs) + { + case 0: + (static_cast(this)->*it->second.pm00)(); + break; + case 1: + (static_cast(this)->*it->second.pm01)(variant_t::create_reference(pd->rgvarg[0])); + break; + case 2: + (static_cast(this)->*it->second.pm02)(variant_t::create_reference(pd->rgvarg[1]), variant_t::create_reference(pd->rgvarg[0])); + break; + case 3: + (static_cast(this)->*it->second.pm03)(variant_t::create_reference(pd->rgvarg[2]), variant_t::create_reference(pd->rgvarg[1]), variant_t::create_reference(pd->rgvarg[0])); + break; + case 4: + (static_cast(this)->*it->second.pm04)(variant_t::create_reference(pd->rgvarg[3]), variant_t::create_reference(pd->rgvarg[2]), variant_t::create_reference(pd->rgvarg[1]), variant_t::create_reference(pd->rgvarg[0])); + break; + default: + return DISP_E_MEMBERNOTFOUND; + } + } + if (pVarResult) *pVarResult = rv.detach(); + } + + + } catch (com_error& err) { + return impl::return_com_error(err); + } catch (const std::exception& err) { + return impl::return_com_error(err); + } catch (HRESULT hr) { + return hr; + } catch (...) { + return E_FAIL; + } + + return S_OK; + + } + + STDMETHOD(GetIDsOfNames)(REFIID, WCHAR** names, unsigned int c, LCID, DISPID* dispid) + { + bool failed = false; + for (size_t i=0; isecond; + } + } + return failed ? DISP_E_UNKNOWNNAME : S_OK; + } + + STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**) + { return E_NOTIMPL; } + + STDMETHOD(GetTypeInfoCount)(UINT *it) + { *it = 0; return S_OK; } + + }; +//@} +} + +#endif diff --git a/lib/local/CamCom/comet/enum.h b/lib/local/CamCom/comet/enum.h new file mode 100644 index 00000000..9d3c6d8c --- /dev/null +++ b/lib/local/CamCom/comet/enum.h @@ -0,0 +1,29 @@ +/** \file + * Legacy header for enum-related implementations and iterators. + * + * This exists for backward compatibility only. Include specific headers + * instead. + */ +/* + * Copyright 2000 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_ENUM_H +#define COMET_ENUM_H + +#include +#include +#include + +#endif diff --git a/lib/local/CamCom/comet/enum_common.h b/lib/local/CamCom/comet/enum_common.h new file mode 100644 index 00000000..b1d1bcdf --- /dev/null +++ b/lib/local/CamCom/comet/enum_common.h @@ -0,0 +1,187 @@ +/** \file + * Code common to enumerator implementations. + */ +/* + * Copyright 2000 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_ENUM_IMPL_H +#define COMET_ENUM_IMPL_H + +#include + +#include +#include +#include + +namespace comet { + + namespace impl { + + template struct type_policy; + + template<> struct type_policy + { + template + static void init(VARIANT& t, const S& s) + { ::VariantInit(&t); t = variant_t::detach( variant_t(s) ); } + + static void clear(VARIANT& t) { ::VariantClear(&t); } + }; + + template<> struct type_policy + { + template + static void init(CONNECTDATA& t, const S& s) + { + t.dwCookie = s.first; + t.pUnk = + com_ptr::detach(com_ptr(s.second)); + } + + static void clear(CONNECTDATA& t) { t.pUnk->Release(); } + }; + + template< + typename Itf, typename T, typename CONVERTER, typename Source> + class enumeration : public simple_object + { + typedef type_policy policy; + public: + /// \name Interface \p Itf + //@{ + STDMETHOD(Next)(ULONG celt, T *rgelt, ULONG* pceltFetched) + { + if (pceltFetched) + *pceltFetched = 0; + if (!rgelt) + return E_POINTER; + + UINT i = 0; + typename Source::const_iterator backup_it_ = source_.current(); + try + { + while (i < celt && source_.current() != source_.end()) + { + policy::init(rgelt[i], converter_(*source_.current())); + ++i; + ++source_.current(); + } + if (pceltFetched) + *pceltFetched = i; + } + catch (...) + { + source_.current() = backup_it_; + for (size_t j = 0; j <= i; ++j) + policy::clear(rgelt[j]); + return E_FAIL; + } + + return i == celt ? S_OK : S_FALSE; + } + + STDMETHOD(Reset)() + { + try + { + source_.current() = source_.begin(); + } + catch (...) { return E_FAIL; } + return S_OK; + } + + STDMETHOD(Skip)(ULONG celt) + { + try + { + while (celt--) source_.current()++; + } + catch (...) { return E_FAIL; } + return S_OK; + } + + STDMETHOD(Clone)(Itf** ppenum) + { + try + { + enumeration* new_enum = + new enumeration(source_, converter_); + new_enum->AddRef(); + *ppenum = new_enum; + } + catch (...) { return E_FAIL; } + return S_OK; + } + //@} + + enumeration( + typename Source source, const CONVERTER& converter) + : source_(source), converter_(converter) {} + + Source source_; + CONVERTER converter_; + + private: + enumeration(const enumeration&); + enumeration& operator=(const enumeration&); + }; + + } + + /** \struct enumerated_type_of enum.h comet/enum.h + * Traits wrapper mapping COM Enumeration interface to element. + */ + template struct enumerated_type_of; + + template<> struct enumerated_type_of + { typedef VARIANT is; }; + + template<> struct enumerated_type_of + { typedef IConnectionPoint* is; }; + + template<> struct enumerated_type_of + { typedef CONNECTDATA is; }; + + /** \struct ptr_converter enum.h comet/enum.h + * IUnknown Converter for containers containing Comet objects. + * \relates stl_enumeration + */ + template struct ptr_converter : std::unary_function< com_ptr, T> + { + com_ptr operator()(const T& x) { return impl::cast_to_unknown(x); } + }; + + /** \struct ptr_converter_select1st enum.h comet/enum.h + * IUnknown Converter for containers containing Comet objects as the first + * elment of a pair. + * \relates stl_enumeration + */ + template struct ptr_converter_select1st : std::unary_function< com_ptr, T> + { + com_ptr operator()(const T& x) { return impl::cast_to_unknown(x.first); } + }; + + /** \struct ptr_converter_select2nd enum.h comet/enum.h + * IUnknown Converter for containers containing Comet objects as the second + * elment of a pair. + * \relates stl_enumeration + */ + template struct ptr_converter_select2nd : std::unary_function< com_ptr, T> + { + com_ptr operator()(const T& x) { return impl::cast_to_unknown(x.second); } + }; +} + +#endif diff --git a/lib/local/CamCom/comet/enum_iterator.h b/lib/local/CamCom/comet/enum_iterator.h new file mode 100644 index 00000000..c95a0180 --- /dev/null +++ b/lib/local/CamCom/comet/enum_iterator.h @@ -0,0 +1,179 @@ +/** \file + * Standard C++ iterators wrapping any COM enumerator + */ +/* + * Copyright 2000 Sofus Mortensen + * Copyright 2011 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_ENUM_ITERATOR_H +#define COMET_ENUM_ITERATOR_H + +#include + +#include +#include + +#include + +namespace comet { + + /** + * STL style iterator for COM enumerator interfaces + */ + template::is> + class enum_iterator : public std::iterator + { + typedef E enumerator_type; + typedef typename enumerated_type_of::is element_type; + typedef impl::type_policy policy; + + com_ptr enum_; + + static value_type policy_init(const element_type& element) + { + value_type out; + policy::init(out, element); + return out; + } + + static value_type copy_value_from_other(const enum_iterator& other) + { + if (other.is_value_set_) + { + value_type v; + policy::init(v, other.value_); + return v; + } + else + { + return value_type(); + } + } + + value_type value_; + + /** + * Flag that ensures the value only gets cleared if it's been set. + * + * Clearing an uninitialised value could be disastrous as it could + * contain any random bits which the clearing code could interpret as + * pointers. + * + * This could happen in the situation where the enumerator has no + * items so the value never gets set. + */ + bool is_value_set_; + + public: + enum_iterator(const com_ptr& e) : + enum_(e), is_value_set_(false) + { + next(); + } + + enum_iterator() : is_value_set_(false) {} + + enum_iterator(const enum_iterator& other) : + enum_(other.enum_), value_(copy_value_from_other(other)), + is_value_set_(other.is_value_set_) {} + + enum_iterator& operator=(const enum_iterator& other) + { + enum_iterator copy(other); + swap(copy); + return *this; + } + + void swap(enum_iterator& other) + { + enum_.swap(other.enum_); + std::swap(value_, other.value_); + } + + /** Move to next element. */ + enum_iterator& operator++() + { + next(); + return *this; + } + + /** Move to next element (post increment). */ + enum_iterator operator++(int) + { + enum_iterator t(*this); + operator++(); + return t; + } + + /** + * Compare against end. + * Comparisons against a non-end iterator throw an exception. + * \todo Look into doing element comparisons. + */ + bool operator!=(const enum_iterator& other) + { + if (enum_ && other.enum_) + throw std::logic_error( + "enum_iterator comparison does not work"); + + return enum_ || other.enum_; + } + + /** Current element. */ + value_type& operator*() + { + return value_; + } + + value_type* operator->() + { + return &value_; + } + + private: + + void next() + { + if (enum_) + { + unsigned long fetched = 0; + element_type pod; + + enum_->Next(1, &pod, &fetched) | raise_exception; + if (fetched == 0) + { + enum_ = NULL; + return; + } + + try + { + if (is_value_set_) + policy::clear(value_); + value_ = policy_init(pod); + is_value_set_ = true; + } + catch (...) + { + policy::clear(pod); + throw; + } + policy::clear(pod); + } + } + }; +} + +#endif diff --git a/lib/local/CamCom/comet/error.h b/lib/local/CamCom/comet/error.h new file mode 100644 index 00000000..b0c86f76 --- /dev/null +++ b/lib/local/CamCom/comet/error.h @@ -0,0 +1,429 @@ +/** \file + * Provide COM Error support. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_ERROR_H +#define COMET_ERROR_H + +#include + +#include +#include + +#include +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable : 4290) + +namespace comet { + + namespace impl { + + inline com_ptr GetErrorInfo() throw() + { + IErrorInfo* ei = 0; + ::GetErrorInfo(0, &ei); + return com_ptr(auto_attach(ei)); + } + + inline com_ptr CreateErrorInfo() throw() + { + ICreateErrorInfo* cei = 0; + ::CreateErrorInfo(&cei); + return com_ptr(auto_attach(cei)); + } + + template inline bool supports_ErrorInfo(Itf* p) + { + com_ptr q = com_cast(com_ptr(p)); + if (q == 0) return false; + return S_OK == q->InterfaceSupportsErrorInfo(uuidof()); + } + + } + + + /** \class com_error error.h comet/error.h + * COM error. + */ + class com_error : public std::runtime_error + { + public: + //! Construct com_error from HRESULT. + /*! + \param hr + HRESULT value of error. + */ + explicit com_error(HRESULT hr) : hr_(hr), std::runtime_error("") + {} + + //! Construct com_error from HRESULT and textual description. + /*! + \param msg + Description of error. + \param hr + HRESULT value of error. + */ + explicit com_error(const bstr_t& msg, HRESULT hr = E_FAIL) : hr_(hr), std::runtime_error("") + { + com_ptr cei(impl::CreateErrorInfo()); + if ( !cei.is_null() ) { + try { + cei->SetDescription(msg.in()); + ei_ = com_ptr( com_cast(cei)) ; + } catch (std::bad_alloc&) + {} + } + } + //! Construct com_error from HRESULT, textual description, source, iid, help. + /*! + \param msg Description of error. + \param hr HRESULT value of error. + \param src Description of source line + \param iid Interface the error was on + \param helpFile Name of help file + \param helpContext Name of help Context + */ + explicit com_error(const bstr_t &msg, HRESULT hr, const bstr_t &src, const uuid_t &iid = uuid_t(), + const bstr_t &helpFile=bstr_t(), DWORD helpContext = -1) + : hr_(hr), std::runtime_error("") + { + com_ptr cei(impl::CreateErrorInfo()); + if ( ! cei.is_null() ) + { + try { + cei->SetDescription(msg.in()); + if (!src.is_null() ) + cei->SetSource( src.in() ); + if (iid != uuid_t()) + cei->SetGUID( iid ); + if (!helpFile.is_null()) + { + cei->SetHelpFile( helpFile.in() ); + cei->SetHelpContext( helpContext ); + } + ei_ = com_ptr( com_cast(cei)) ; + } catch (std::bad_alloc&) + {} + } + } + + /// Construct with an error-info and hresult. + explicit com_error(HRESULT hr, const com_ptr& ei) + : hr_(hr), ei_(ei), std::runtime_error("") + {} + + public: + //! Return a string with a description of the error + /*! + what() uses Description from IErrorInfo if such is present, otherwise FormatMessage is used + to create a description of the HRESULT error value. + + \retval + A const char* string with a textual description of the error. + */ + const char* what() const throw() + { + try { + if (what_.empty()) { + what_ = s_str(); + } + } catch (std::bad_alloc&) { + return 0; + } + + return what_.c_str(); + } + /// Returns a std::string with a description of the error. + std::string s_str() const + { + std::string ret; + get_str(ret); + return ret; + } + /// Returns a std::wstring with a description of the error. + std::wstring w_str() const + { + std::wstring ret; + get_str(ret); + return ret; + } + /// Returns a tstring with a description of the error. + tstring t_str() const + { + tstring ret; + get_str(ret); + return ret; + } + + private: + void get_str(std::string& ret) const + { + if (ei_.is_null() == false) + { + bstr_t bs; + if (SUCCEEDED(ei_->GetDescription(bs.out())) && !bs.is_empty()) + { + ret= bs.s_str(); + return; + } + } + + char* lpMsgBuf; + if (FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, hr_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + reinterpret_cast(&lpMsgBuf), 0, NULL)) + { + char* lpEnd = lpMsgBuf; + while (*lpEnd != '\0') + lpEnd++; + + while (lpEnd > lpMsgBuf && // Trim trailing newlines + (*(lpEnd - 1) == '\n' || *(lpEnd - 1) == '\r')) + lpEnd--; + + ret.assign(lpMsgBuf, lpEnd - lpMsgBuf); + LocalFree(lpMsgBuf); + } + } + + void get_str(std::wstring& ret) const + { + if (ei_.is_null() == false) + { + bstr_t bs; + if (SUCCEEDED(ei_->GetDescription(bs.out())) && !bs.is_empty()) + { + ret = bs.w_str(); + return; + } + } + + wchar_t* lpMsgBuf; + if (FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, hr_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + reinterpret_cast(&lpMsgBuf), 0, NULL)) + { + wchar_t* lpEnd = lpMsgBuf; + while (*lpEnd != L'\0') + lpEnd++; + + while (lpEnd > lpMsgBuf && // Trim trailing newlines + (*(lpEnd - 1) == L'\n' || *(lpEnd - 1) == L'\r')) + lpEnd--; + + ret.assign(lpMsgBuf, lpEnd - lpMsgBuf); + LocalFree(lpMsgBuf); + } + } + + public: + + /** Return the HRESULT for the error. + */ + HRESULT hr() const throw() + { + return hr_; + } + + /// \name wrappers for IErrorInfo + //@{ + + /// Description of the error. + bstr_t description() const + { + bstr_t rv; + if (!ei_.is_null()) ei_->GetDescription( rv.out() ) | raise_exception; + return rv; + } + + /// The error source description. + bstr_t source() const + { + bstr_t rv; + if (!ei_.is_null()) ei_->GetSource( rv.out() ) | raise_exception; + return rv; + } + + /// Interface IID. + GUID guid() const + { + GUID rv; + if (!ei_.is_null()) ei_->GetGUID( &rv ) | raise_exception; + else ZeroMemory(&rv, sizeof(rv)); + return rv; + } + + /// Helpfile name. + bstr_t help_file() const + { + bstr_t rv; + if (!ei_.is_null()) ei_->GetHelpFile( rv.out() ) | raise_exception; + return rv; + } + + + /// Help conext. + DWORD help_context() const + { + DWORD rv = 0; + if (!ei_.is_null()) ei_->GetHelpContext( &rv ) | raise_exception; + return rv; + } + //@} + + /// Return the error-info object. + com_ptr get_ei() const + { + return ei_; + } + + private: + mutable std::string what_; + com_ptr ei_; + HRESULT hr_; + }; + + + namespace impl { + + inline HRESULT return_com_error(HRESULT hr, const bstr_t &desc, const bstr_t &src = auto_attach(BSTR(NULL)), + const uuid_t &iid=CLSID_NULL, const bstr_t &helpFile=bstr_t(), DWORD helpContext = -1) + { + com_ptr cei(impl::CreateErrorInfo()); + if (cei.is_null() == false) { + try { + cei->SetDescription(desc.in()); + if (!src.is_null() ) + cei->SetSource( src.in() ); + if (iid != uuid_t()) + cei->SetGUID( iid ); + if (!helpFile.is_null()) + { + cei->SetHelpFile( helpFile.in() ); + cei->SetHelpContext( helpContext ); + } + com_ptr ei = com_cast(cei); + ::SetErrorInfo(0, ei.in()); + } catch (std::bad_alloc&) + {} + } + return hr; + } + inline HRESULT return_com_error(const std::exception& err, const bstr_t &src = auto_attach(BSTR(NULL)), + const uuid_t &iid=CLSID_NULL, const bstr_t &helpFile=bstr_t(), DWORD helpContext = -1) + { + return return_com_error( E_FAIL, bstr_t(err.what()),src,iid,helpFile, helpContext ); + } + inline HRESULT return_com_error(const com_error& err, const bstr_t &src = bstr_t(), const uuid_t &iid = CLSID_NULL) throw() + { + const bstr_t &cursrc =err.source(); + const uuid_t &curiid =err.guid(); + // Return error info with more info. + return return_com_error( err.hr(), err.description(), cursrc.is_null()?src:cursrc, + curiid.is_null()?iid:curiid, err.help_file(), err.help_context()); + } + + + inline void throw_com_error_(HRESULT hr, const com_ptr& ei) + { + throw_error_handler::throw_error(hr, ei); + } + + // Raising an HRESULT with a message + inline void raise_exception_t::operator()(const bstr_t& msg, HRESULT hr/* = E_FAIL*/) const + { + throw com_error(msg, hr); + } + + // Raising an HRESULT + inline void raise_exception_t::operator()(HRESULT hr) const + { + throw com_error(hr); + } + + } // namespace impl + + /*! \addtogroup ErrorHandling + */ + //@{ + + /** Throw COM error using com_error, using HRESULT and IErrorInfo. + */ + template inline void throw_com_error(Itf* p, HRESULT hr) + { + if (impl::supports_ErrorInfo(p)) + { + com_ptr ei = impl::GetErrorInfo(); + if (ei.is_null() == false) impl::throw_com_error_(hr, ei); + } + throw com_error(hr); + } + + template + inline void throw_error_handler::throw_error(HRESULT hr, const com_ptr &ei) + { + throw com_error(hr, ei); + } + + /** + * Construct exception for a method called on a raw COM interface. + * + * This method aims to imbue the com_error with as much information about + * the failure as possible: + * - If the interface supports IErrorInfo, the information is taken from + * the last ErrorInfo set on the current thread. + * - If not, the HRESULT alone determines the message. + * + * Use this constructor immediately after an interface returns a + * failure code before any other code can call SetErrorInfo and + * overwrite the error. + * + * \param failure_source Interface operator method returned a failure code. + * \param hr HRESULT value of error. + * + * \todo Can we add an optional user-defined message to this? + */ + template + com_error com_error_from_interface(Itf* failure_source, HRESULT hr) + { + if (impl::supports_ErrorInfo(failure_source)) + return com_error(hr, impl::GetErrorInfo()); + else + return com_error(hr); + } + + template + com_error com_error_from_interface( + com_ptr failure_source, HRESULT hr) + { + return com_error_from_interface(failure_source.get(), hr); + } + + //@} +} + +#pragma warning(pop) + +#endif diff --git a/lib/local/CamCom/comet/error_fwd.h b/lib/local/CamCom/comet/error_fwd.h new file mode 100644 index 00000000..9a71c8cd --- /dev/null +++ b/lib/local/CamCom/comet/error_fwd.h @@ -0,0 +1,119 @@ +/** \file + * Error forwarding - using operator|. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_ERROR_FWD_H +#define COMET_ERROR_FWD_H + +#include + +#include +#include + +#include + +#pragma warning(push) +#pragma warning(disable : 4290) + +namespace comet { + template class com_ptr; + class bstr_t; + + class com_error; + /*! \addtogroup ErrorHandling + */ + //@{ + namespace impl { + class raise_exception_t + { + public: + // Raising a std::exception + // inline void operator()(const std::exception& err) const; + + // Raising an HRESULT with a message + inline void operator()(const bstr_t& msg, HRESULT hr = E_FAIL) const; + + // Raising an HRESULT + inline void operator()(HRESULT hr) const; + }; + + } + + //! Raise exception "function" + /*! + raise_exception can by used in two different ways: + + First example. The 'do or die' paradigm using overloaded operator|. + Here try() is a function that returns an HRESULT. If try fails an exception will be raised. + \code + try() | raise_exception; + \endcode + + Second example. Using raise_exception as an exception throwing function. + \code + raise_exception(E_POINTER); + raise_exception(L"Ran out of toilet paper!", E_FAIL); + \endcode + + */ + static impl::raise_exception_t raise_exception; + + //! Overload for the 'do or die' useage of raise_exception. + /*! + \code + try() | raise_exception; + \endcode + */ + + namespace impl{ + inline HRESULT operator|(HRESULT hr, const impl::raise_exception_t&) + {if (FAILED(hr)) raise_exception(hr); return hr; } + } + using impl::operator|; + + /**! \struct throw_error_handler error_fwd.h comet\error_fwd.h + * Overridable error info handling. + * To override the error handling, use code like this, making sure + * that you define the handler before including comet/error.h. + \code + #include + template<> struct throw_error_handler + { + static inline void throw_error(HRESULT hr, const com_ptr &ei); + }; + #include + // #include + template<> + inline void throw_error_handler::throw_error(HRESULT hr, const com_ptr &ei) + { + .... Override here .... + } + \endcode + */ + + template + struct throw_error_handler + { + /// Called to throw the error. + static inline void throw_error(HRESULT hr, const com_ptr &ei); + }; + //@} +} + +#pragma warning(pop) + +#endif diff --git a/lib/local/CamCom/comet/exe_server.h b/lib/local/CamCom/comet/exe_server.h new file mode 100644 index 00000000..5d53fae8 --- /dev/null +++ b/lib/local/CamCom/comet/exe_server.h @@ -0,0 +1,292 @@ +/** \file + * exe-server classes. + */ +/* + * Copyright 2001, 2002 Mikael Lindgren, Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_EXE_SERVER_H +#define COMET_EXE_SERVER_H + +#include +#include + +namespace comet { + + namespace impl { + template struct register_class_entry + { + typedef COMET_STRICT_TYPENAME CLS_LIST::head CLASS; + typedef COMET_STRICT_TYPENAME CLS_LIST::tail NEXT; + + template struct register_info + { + DWORD id; + }; + + static register_info& get_register_info() + { + static register_info info; + return info; + } + COMET_FORCEINLINE static HRESULT register_class_object(DWORD context, DWORD flags) + { + const IID& clsid = comtype::uuid(); + ::IUnknown* p = impl::coclass_table_entry::factory::get(clsid); + if (p) + { + HRESULT hr = ::CoRegisterClassObject(clsid, p, context, flags, &get_register_info().id); + p->Release(); + if (hr != S_OK) + return hr; + } + return register_class_entry::register_class_object(context, flags); + } + + COMET_FORCEINLINE static void revoke_class_object() + { + ::CoRevokeClassObject(get_register_info().id); + register_class_entry::revoke_class_object(); + } + }; + + template <> struct register_class_entry + { + COMET_FORCEINLINE static HRESULT register_class_object(DWORD context, DWORD flags) + { + COMET_NOTUSED(context); + COMET_NOTUSED(flags); + return S_OK; + } + + COMET_FORCEINLINE static void revoke_class_object() + { + } + }; + }; + + /*! \addtogroup Server + */ + //@{ + + //! Define an EXE server + template > class exe_server : private thread + { +#if !(_WIN32_WINNT >= 0x0400 ) && !defined(_WIN32_DCOM) + // Implementing a free threaded exe server requires NT 4.0 or better. + COMET_STATIC_ASSERT(FREE_THREADED == false); +#endif + + typedef coclass_table COCLASS_TABLE; + + enum { terminate_pause = 1000, idle_shutdown_time = 5000 }; + + public: + exe_server(HINSTANCE instance); + exe_server(HINSTANCE instance, const GUID& appid, const tstring& appid_descr); + ~exe_server(); + + HRESULT run(); + + HRESULT register_server(); + HRESULT unregister_server(); + HRESULT register_class_objects(DWORD context, DWORD flags); + void revoke_class_objects(); + + private: + event shutdown_event_; + DWORD main_thread_id_; + const GUID* appid_; + tstring appid_descr_; + + virtual DWORD thread_main(); + }; + //@} + + template + exe_server::exe_server(HINSTANCE instance): + main_thread_id_(::GetCurrentThreadId()), + appid_(0) + { + module_t& m = module(); + + m.instance(instance); + m.set_shutdown_event(shutdown_event_); + + // initialize static variables in factory::get to avoid potential thread safety problem. + ::IUnknown* cf = COCLASS_TABLE::find(IID_NULL); + } + + template + exe_server::exe_server(HINSTANCE instance, const GUID& appid, const tstring& appid_descr): + main_thread_id_(::GetCurrentThreadId()), + appid_(&appid), appid_descr_(appid_descr) + { + module_t& m = module(); + + m.instance(instance); + m.set_shutdown_event(shutdown_event_); + + // initialize static variables in factory::get to avoid potential thread safety problem. + ::IUnknown* cf = COCLASS_TABLE::find(IID_NULL); + } + + template + exe_server::~exe_server() + { + module().clear_shutdown_event(); + } + + template + HRESULT exe_server::run() + { + thread::start(); + + HRESULT hr; +#if (_WIN32_WINNT >= 0x0400 ) || defined(_WIN32_DCOM) // DCOM + if (FREE_THREADED) + { + hr = register_class_objects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE|REGCLS_SUSPENDED); + if (SUCCEEDED(hr)) + hr = ::CoResumeClassObjects(); + } + else +#endif + hr = register_class_objects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE); + + if (SUCCEEDED(hr)) + { + MSG msg; + while (::GetMessage(&msg, 0, 0, 0)) + ::DispatchMessage(&msg); + } + else if (thread::running()) + { + shutdown_event_.set(); + thread::wait(0); + } + + revoke_class_objects(); + Sleep(terminate_pause); //wait for any threads to finish + + module().shutdown(); + + return hr; + } + + template + HRESULT exe_server::register_server() + { + TCHAR filename[MAX_PATH]; + + GetModuleFileName(module().instance(), filename, MAX_PATH); + + { + HRESULT hr = impl::typelibrary_registration::perform(filename, false); + if(FAILED(hr)) return SELFREG_E_TYPELIB; + } + + try { + if (appid_) + { + tstring key(_T("AppID\\{") + uuid_t(*appid_).str() + _T("}")); + + regkey rkey(HKEY_CLASSES_ROOT); + rkey.create(key)[_T("")] = appid_descr_; + } + COCLASS_TABLE::registration(filename, false, false, appid_); + } + catch (const com_error &e) + { + unregister_server(); + return impl::return_com_error(e); + } + catch (const std::exception &e) + { + unregister_server(); + ::OutputDebugStringA(e.what()); + return E_FAIL; + } + + return S_OK; + } + + template + HRESULT exe_server::unregister_server() + { + TCHAR filename[MAX_PATH]; + GetModuleFileName(module().instance(), filename, MAX_PATH); + + impl::typelibrary_registration::perform(filename, true); + + if (appid_) + { + tstring key(_T("AppID\\{") + uuid_t(*appid_).str() + _T("}")); + + regkey rkey(HKEY_CLASSES_ROOT); + rkey.delete_subkey_nothrow(key); + } + COCLASS_TABLE::registration(filename, true, false, appid_); + return S_OK; + } + + template + HRESULT exe_server::register_class_objects(DWORD context, DWORD flags) + { + return impl::register_class_entry::register_class_object(context, flags); + } + + template + void exe_server::revoke_class_objects() + { + impl::register_class_entry::revoke_class_object(); + } + + template + DWORD exe_server::thread_main() + { + module_t& m = module(); + + while (1) + { + shutdown_event_.wait(); + do + { + m.reset_activity_flag(); + } while (shutdown_event_.wait(idle_shutdown_time)); + // timed out + + if (!m.has_activity()) // if no activity let's really bail + { +#if (_WIN32_WINNT >= 0x0400 ) || defined(_WIN32_DCOM) // DCOM + if (FREE_THREADED) + { + ::CoSuspendClassObjects(); + if (!m.has_activity()) + break; + } + else + break; +#else + break; +#endif + } + } + ::PostThreadMessage(main_thread_id_, WM_QUIT, 0, 0); + return 0; + } +}; + + +#endif diff --git a/lib/local/CamCom/comet/functor.h b/lib/local/CamCom/comet/functor.h new file mode 100644 index 00000000..56a36f37 --- /dev/null +++ b/lib/local/CamCom/comet/functor.h @@ -0,0 +1,1341 @@ +/** \file + * Functor implementation. + * + * functor.h is based on the functor library presented in Chapter 5 of + * "Modern C++ Design" by Andrei Alexandrescu. + * + */ + +/* + * Copyright 2001 Sofus Mortensen, Michael Geddes + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_FUNCTOR_H +#define COMET_FUNCTOR_H + +#include +#include +#include +#include + +namespace comet { + /*! \addtogroup Misc + */ + //@{ + +#ifdef COMET_PARTIAL_SPECIALISATION + +namespace detail { + + template struct parameter_type_aux + { + typedef const T& result; + }; + + template struct parameter_type_aux + { + typedef T result; + }; + +}; + +/** \struct parameter_type functor.h comet/functor.h + * Gives us a reference to a type. If the type is already a reference, then + * just returns the type. + */ +template struct parameter_type +{ + enum { is_ref = type_traits::is_reference::value }; + /// A reference to \p T or \p T if it is already a reference. + typedef typename detail::parameter_type_aux< (is_ref!=0) ,T >::result result; +}; +#else // COMET_PARTIAL_SPECIALISATION +namespace detail { + + template struct parameter_type_aux + { + template struct X + { + typedef const T& result; + }; + }; + + template<> struct parameter_type_aux + { + template struct X + { + typedef T result; + }; + }; + +}; + +template struct parameter_type +{ + enum { is_ref = type_traits::is_reference::value }; + typedef detail::parameter_type_aux< (is_ref!=0) >::X::result result; +}; +#endif // COMET_PARTIAL_SPECIALISATION + +#ifndef COMET_PARTIAL_SPECIALISATION + +#define COMET_PARTIAL_NAME( name ) X +#define COMET_PARTIAL_NS_1(A) >::X +#define COMET_PARTIAL_NS_2(A,B) >::X +#define COMET_PARTIAL_NS_3(A,B,C) >::X + +#define COMET_DEFINE_PARTIAL( X1, X2 , name) \ +template < X1 > struct name\ +{ \ + template < X2 > class X + +#define COMET_SPECIALISE_PARTIAL( SX1, X2 , name) \ +template <> struct name\ +{ \ + template < X2 > class X + +#define COMET_DEFINE_PARTIAL2( X1, X2, X3 , name) \ +template < X1 > struct name\ +{ \ + template < X2, X3 > class X + +#define COMET_SPECIALISE_PARTIAL2( SX1, X2, X3 , name) \ +template <> struct name\ +{ \ + template < X2, X3 > class X + +#define COMET_DEFINE_PARTIAL3( X1, X2, X3, X4 , name) \ +template < X1 > struct name\ +{ \ + template < X2, X3, X4 > class X + +#define COMET_SPECIALISE_PARTIAL3( SX1, X2, X3, X4 , name) \ +template <> struct name\ +{ \ + template < X2, X3, X4 > class X + + +#define COMET_CLOSE_PARTIAL() }; + +#else + +#define COMET_PARTIAL_NAME( name ) name +#define COMET_PARTIAL_NS_1(A) ,A> +#define COMET_PARTIAL_NS_2(A,B) ,A,B> +#define COMET_PARTIAL_NS_3(A,B,C) ,A,B,C> + +#define COMET_CLOSE_PARTIAL() + +#define COMET_PARTIAL_CONSTRUCTOR( name ) name + +#define COMET_DEFINE_PARTIAL(TN1, X1, TN2, X2 , name) \ +template class name + +#define COMET_SPECIALISE_PARTIAL( SX1, TN2,X2 , name) \ +template < TN2 X2 > class name + +#define COMET_DEFINE_PARTIAL2( TN1,X1,TN2,X2,TN3,X3 , name) \ +template class name + +#define COMET_SPECIALISE_PARTIAL2( SX1, TN2,X2, TN3,X3 , name) \ +template < TN2 X2, TN3 X3 > class name + +#define COMET_DEFINE_PARTIAL3(TN1,X1, TN2,X2, TN3,X3, TN4,X4 , name) \ +template < TN1 X1, TN2 X2, TN3 X3,TN4 X4 > class name + +#define COMET_SPECIALISE_PARTIAL3( SX1, TN2,X2, TN3,X3, TN4,X4 , name) \ +template < TN2 X2, TN3 X3, TN4 X4 > class name + +#endif + +/** \struct parameter_types functor.h comet/functor.h + * Gives the parameter type of parameters 1 thru 16. + * This gaurantees the arguments pass by reference, without breaking if it is already a + * reference. + * \sa parameter_type + */ +template struct parameter_types +{ + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_1; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_2; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_3; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_4; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_5; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_6; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_7; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_8; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_9; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_10; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_11; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_12; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_13; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_14; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_15; + typedef typename parameter_type< typename typelist::type_at::result >::result PARM_16; +}; + +template class functor_impl; + + +/** \struct functor_impl_aux functor.h comet/functor.h + * Virtual interface for functor implementation. + * Provide partial specialisation of functor_impl to parameter type. + * \param L number of arguments. + * \param R Return type. + * \param LIST Argument list. + */ +COMET_DEFINE_PARTIAL2( int,L,typename,R,typename,LIST,functor_impl_aux) : public parameter_types +{ + /** Virtual override to call the function. + * \param arguments Whatever arguments provided by \a LIST + * \return Return type is \a R + */ + virtual R operator()( nil arguments ) = 0; + + /** Clone the functor. (easiest way of managing memory - should only be + * a light implementation anyway). + */ + virtual functor_impl* clone() const = 0; + /** Provide virtual destruction. + */ + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + + +COMET_SPECIALISE_PARTIAL2( 0, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(1, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(2, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(3, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(4, typename,R, typename,LIST,functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(5,typename,R, typename,LIST,functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(6, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(7, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(8, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7, PARM_8 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(9, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7, PARM_8, PARM_9 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(10, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7, PARM_8, PARM_9, PARM_10 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(11, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7, PARM_8, PARM_9, PARM_10, PARM_11 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(12, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7, PARM_8, PARM_9, PARM_10, PARM_11, PARM_12 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(13, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7, PARM_8, PARM_9, PARM_10, PARM_11, PARM_12, PARM_13 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(14, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7, PARM_8, PARM_9, PARM_10, PARM_11, PARM_12, PARM_13, PARM_14 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(15, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7, PARM_8, PARM_9, PARM_10, PARM_11, PARM_12, PARM_13, PARM_14, PARM_15 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2(16, typename,R, typename,LIST, functor_impl_aux) : public parameter_types +{ +public: + virtual R operator()( PARM_1, PARM_2, PARM_3, PARM_4, PARM_5, PARM_6, PARM_7, PARM_8, PARM_9, PARM_10, PARM_11, PARM_12, PARM_13, PARM_14, PARM_15, PARM_16 ) = 0; + virtual functor_impl* clone() const = 0; + virtual ~COMET_PARTIAL_NAME(functor_impl_aux)() {} +}; +COMET_CLOSE_PARTIAL() + + +/** \struct functor_handler functor.h comet/functor.h + * Provide implementation of functor_impl virtual type for functions. + * The implementation is effectively 'partially specialised' to return type. + */ +COMET_DEFINE_PARTIAL2(typename,RT, typename,PF, typename,FUN, functor_handler) + : public functor_impl< typename PF::result_type, typename PF::parm_list > +{ +public: + typedef typename PF::result_type result_type; + + COMET_PARTIAL_NAME(functor_handler)(const FUN& fun) : fun_(fun) {} + functor_impl< typename PF::result_type, typename PF::parm_list >* clone() const + { return new COMET_PARTIAL_NAME(functor_handler)(*this); } + + result_type operator()( ) + { return fun_( ); } + + result_type operator()( typename PF::PARM_1 p1 ) + { return fun_( p1 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2 ) + { return fun_( p1, p2 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3 ) + { return fun_( p1, p2, p3 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4 ) + { return fun_( p1, p2, p3, p4 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5 ) + { return fun_( p1, p2, p3, p4, p5 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6 ) + { return fun_( p1, p2, p3, p4, p5, p6 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7, p8 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14, typename PF::PARM_15 p15 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14, typename PF::PARM_15 p15, typename PF::PARM_16 p16 ) + { return fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); } + +private: + FUN fun_; + +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL2( void, typename,PF, typename,FUN , functor_handler) + : public functor_impl< typename PF::result_type, typename PF::parm_list > +{ +public: + typedef typename PF::result_type result_type; + + COMET_PARTIAL_NAME(functor_handler)(const FUN& fun) : fun_(fun) {} + + functor_impl< typename PF::result_type, typename PF::parm_list >* clone() const + { return new COMET_PARTIAL_NAME(functor_handler)(*this); } + result_type operator()( ) + { fun_( ); } + + result_type operator()( typename PF::PARM_1 p1 ) + { fun_( p1 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2 ) + { fun_( p1, p2 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3 ) + { fun_( p1, p2, p3 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4 ) + { fun_( p1, p2, p3, p4 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5 ) + { fun_( p1, p2, p3, p4, p5 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6 ) + { fun_( p1, p2, p3, p4, p5, p6 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7 ) + { fun_( p1, p2, p3, p4, p5, p6, p7 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8 ) + { fun_( p1, p2, p3, p4, p5, p6, p7, p8 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9 ) + { fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10 ) + { fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11 ) + { fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12 ) + { fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13 ) + { fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14 ) + { fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14, typename PF::PARM_15 p15 ) + { fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14, typename PF::PARM_15 p15, typename PF::PARM_16 p16 ) + { fun_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); } + + +private: + FUN fun_; + +}; +COMET_CLOSE_PARTIAL() + +/** \struct memfun_handler functor.h comet/functor.h + * Provide implementation of functor_impl virtual type for calling a member on + * an object. + * The implementation is effectively 'partially specialised' to return type. + * \param RT Return type + * \param PF Parameter types + * \param OBJ_PTR Object pointer type + * \param MEMFN_PTR Function pointer type + */ +COMET_DEFINE_PARTIAL3(typename,RT,typename,PF, typename,OBJ_PTR, typename,MEMFN_PTR , memfun_handler) + : public functor_impl +{ +private: + OBJ_PTR obj_; + MEMFN_PTR memfn_; + +public: + typedef typename PF::result_type result_type; + + COMET_PARTIAL_NAME(memfun_handler)(const OBJ_PTR& obj, MEMFN_PTR memfn) : obj_(obj), memfn_(memfn) {} + + functor_impl* clone() const + { return new COMET_PARTIAL_NAME(memfun_handler)(*this); } + + result_type operator()( ) + { return ((*obj_).*memfn_)( ); } + + result_type operator()( typename PF::PARM_1 p1 ) + { return ((*obj_).*memfn_)( p1 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2 ) + { return ((*obj_).*memfn_)( p1, p2 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3 ) + { return ((*obj_).*memfn_)( p1, p2, p3 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14, typename PF::PARM_15 p15 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14, typename PF::PARM_15 p15, typename PF::PARM_16 p16 ) + { return ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); } + +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL3( void, typename,PF, typename,OBJ_PTR, typename,MEMFN_PTR , memfun_handler) + : public functor_impl +{ +public: + typedef typename PF::result_type result_type; + + COMET_PARTIAL_NAME(memfun_handler)(const OBJ_PTR& obj, MEMFN_PTR memfn) : obj_(obj), memfn_(memfn) {} + + functor_impl* clone() const + { return new COMET_PARTIAL_NAME(memfun_handler)(*this); } + + result_type operator()( ) + { ((*obj_).*memfn_)( ); } + + result_type operator()( typename PF::PARM_1 p1 ) + { ((*obj_).*memfn_)( p1 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2 ) + { ((*obj_).*memfn_)( p1, p2 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3 ) + { ((*obj_).*memfn_)( p1, p2, p3 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14, typename PF::PARM_15 p15 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); } + + result_type operator()( typename PF::PARM_1 p1, typename PF::PARM_2 p2, typename PF::PARM_3 p3, typename PF::PARM_4 p4, typename PF::PARM_5 p5, typename PF::PARM_6 p6, typename PF::PARM_7 p7, typename PF::PARM_8 p8, typename PF::PARM_9 p9, typename PF::PARM_10 p10, typename PF::PARM_11 p11, typename PF::PARM_12 p12, typename PF::PARM_13 p13, typename PF::PARM_14 p14, typename PF::PARM_15 p15, typename PF::PARM_16 p16 ) + { ((*obj_).*memfn_)( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); } + +private: + OBJ_PTR obj_; + MEMFN_PTR memfn_; +}; +COMET_CLOSE_PARTIAL() + +/** \class functor_impl functor.h comet/functor.h + * Implementation of a functor. + * \param R Return type. + * \param LIST List of argument types. + */ +template +class ATL_NO_VTABLE functor_impl : public functor_impl_aux< typelist::length::value COMET_PARTIAL_NS_2(R,LIST) +{}; + +template class functor; + +/** Provide various operator() versions to call the virtual functor. + * This is Effectively paritally specialised to return type (for void + * implementation). + * \param R Return type. + * \param LIST argument list. + */ +COMET_DEFINE_PARTIAL( typename,R,typename,LIST, functor_operators ) : public parameter_types +{ + typedef functor BASE; +public: + R operator()( ) + { return (*(static_cast(this)->impl()))( ); } + + R operator()( PARM_1 p1 ) + { return (*(static_cast(this)->impl()))( p1 ); } + + R operator()( PARM_1 p1, PARM_2 p2 ) + { return (*(static_cast(this)->impl()))( p1, p2 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12, PARM_13 p13 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12, PARM_13 p13, PARM_14 p14 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12, PARM_13 p13, PARM_14 p14, PARM_15 p15 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); } + + R operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12, PARM_13 p13, PARM_14 p14, PARM_15 p15, PARM_16 p16 ) + { return (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); } + +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL( void,typename,LIST , functor_operators) + : public parameter_types +{ + typedef functor BASE; +public: + + void operator()( ) + { (*(static_cast(this)->impl()))( ); } + + void operator()( PARM_1 p1 ) + { (*(static_cast(this)->impl()))( p1 ); } + + void operator()( PARM_1 p1, PARM_2 p2 ) + { (*(static_cast(this)->impl()))( p1, p2 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3 ) + { (*(static_cast(this)->impl()))( p1, p2, p3 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12, PARM_13 p13 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12, PARM_13 p13, PARM_14 p14 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12, PARM_13 p13, PARM_14 p14, PARM_15 p15 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); } + + void operator()( PARM_1 p1, PARM_2 p2, PARM_3 p3, PARM_4 p4, PARM_5 p5, PARM_6 p6, PARM_7 p7, PARM_8 p8, PARM_9 p9, PARM_10 p10, PARM_11 p11, PARM_12 p12, PARM_13 p13, PARM_14 p14, PARM_15 p15, PARM_16 p16 ) + { (*(static_cast(this)->impl()))( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); } + +}; +COMET_CLOSE_PARTIAL() + +/** \class functor functor.h comet/functor.h + * Functor pointer class and implementation factory. This provides the interface to the functor library. + * \param R Return type + * \param LIST List of types using \link make_list comet::make_list \endlink + */ +template class functor : public functor_operators IMPL; + + typedef R result_type; + typedef LIST parm_list; + + functor() {}; + functor(const functor& f) : impl_((f.impl_.get()== NULL) ? NULL: f.impl_->clone()) {} + explicit functor(std::auto_ptr< IMPL > impl) : impl_(impl) {} + + template explicit functor(int, FUN fun) : impl_(new functor_handler), FUN)(fun)) + {} + + template functor(int, const OBJ_PTR& obj, MEMFN_PTR memfn) + : impl_(new memfun_handlerCOMET_PARTIAL_NS, OBJ_PTR, MEMFN_PTR>(obj, memfn)) + {} + + functor& operator=(const functor& rhs) + { + std::auto_ptr tmp((rhs.impl_.get() == NULL)? NULL: rhs.impl_->clone()); + impl_ = tmp; + return *this; + } + + std::auto_ptr &impl(){return impl_;} + + bool is_null() const { return impl_.get() == NULL; } +protected: + std::auto_ptr impl_; +}; + +/** \class chainer functor.h comet/functor.h + * Implements a functor that chains one functor to another functor. + * \param R Return type + * \param LIST Typelist of arguments + * \sa chain + */ +COMET_DEFINE_PARTIAL(typename,R, typename,LIST, chainer) : public functor_impl +{ +private: +typedef functor FUN; + +FUN fun1_; +FUN fun2_; + + +public: +COMET_PARTIAL_NAME(chainer)(const FUN& fun1, const FUN& fun2) : fun1_(fun1), fun2_(fun2) +{} + +COMET_PARTIAL_NAME(chainer)(const COMET_PARTIAL_NAME(chainer)& x) : fun1_(x.fun1_), fun2_(x.fun2_) +{} + +functor_impl* clone() const +{ return new COMET_PARTIAL_NAME(chainer)(*this); } + + R operator()( ) + { + fun1_( ); + return fun2_( ); + } + + R operator()( typename FUN::PARM_1 p1 ) + { + fun1_( p1 ); + return fun2_( p1 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2 ) + { + fun1_( p1, p2 ); + return fun2_( p1, p2 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3 ) + { + fun1_( p1, p2, p3 ); + return fun2_( p1, p2, p3 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4 ) + { + fun1_( p1, p2, p3, p4 ); + return fun2_( p1, p2, p3, p4 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5 ) + { + fun1_( p1, p2, p3, p4, p5 ); + return fun2_( p1, p2, p3, p4, p5 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6 ) + { + fun1_( p1, p2, p3, p4, p5, p6 ); + return fun2_( p1, p2, p3, p4, p5, p6 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7, p8 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12, typename FUN::PARM_13 p13 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12, typename FUN::PARM_13 p13, typename FUN::PARM_14 p14 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12, typename FUN::PARM_13 p13, typename FUN::PARM_14 p14, typename FUN::PARM_15 p15 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); + } + + R operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12, typename FUN::PARM_13 p13, typename FUN::PARM_14 p14, typename FUN::PARM_15 p15, typename FUN::PARM_16 p16 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); + return fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); + } + + +}; +COMET_CLOSE_PARTIAL() + +/** Specialisation for void. + */ +COMET_SPECIALISE_PARTIAL( void, typename,LIST , chainer ) + : public functor_impl +{ +private: + typedef functor FUN; + + FUN fun1_; + FUN fun2_; + + +public: + COMET_PARTIAL_NAME(chainer)(const FUN& fun1, const FUN& fun2) : fun1_(fun1), fun2_(fun2) + {} + + COMET_PARTIAL_NAME(chainer)(const COMET_PARTIAL_NAME(chainer)& x) : fun1_(x.fun1_), fun2_(x.fun2_) + {} + + functor_impl* clone() const + { return new COMET_PARTIAL_NAME(chainer)(*this); } + + void operator()( ) + { + fun1_( ); + fun2_( ); + } + + void operator()( typename FUN::PARM_1 p1 ) + { + fun1_( p1 ); + fun2_( p1 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2 ) + { + fun1_( p1, p2 ); + fun2_( p1, p2 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3 ) + { + fun1_( p1, p2, p3 ); + fun2_( p1, p2, p3 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4 ) + { + fun1_( p1, p2, p3, p4 ); + fun2_( p1, p2, p3, p4 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5 ) + { + fun1_( p1, p2, p3, p4, p5 ); + fun2_( p1, p2, p3, p4, p5 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6 ) + { + fun1_( p1, p2, p3, p4, p5, p6 ); + fun2_( p1, p2, p3, p4, p5, p6 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7 ); + fun2_( p1, p2, p3, p4, p5, p6, p7 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8 ); + fun2_( p1, p2, p3, p4, p5, p6, p7, p8 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); + fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); + fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); + fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); + fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12, typename FUN::PARM_13 p13 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); + fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12, typename FUN::PARM_13 p13, typename FUN::PARM_14 p14 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); + fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12, typename FUN::PARM_13 p13, typename FUN::PARM_14 p14, typename FUN::PARM_15 p15 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); + fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ); + } + + void operator()( typename FUN::PARM_1 p1, typename FUN::PARM_2 p2, typename FUN::PARM_3 p3, typename FUN::PARM_4 p4, typename FUN::PARM_5 p5, typename FUN::PARM_6 p6, typename FUN::PARM_7 p7, typename FUN::PARM_8 p8, typename FUN::PARM_9 p9, typename FUN::PARM_10 p10, typename FUN::PARM_11 p11, typename FUN::PARM_12 p12, typename FUN::PARM_13 p13, typename FUN::PARM_14 p14, typename FUN::PARM_15 p15, typename FUN::PARM_16 p16 ) + { + fun1_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); + fun2_( p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); + } + +}; +COMET_CLOSE_PARTIAL() + +/** \class binder_first functor.h comet/functor.h + * Implements a functor that calls the first parameter bound to a specified + * type. The functor type implemented is effectively functor. + * \sa bind_first + */ +COMET_DEFINE_PARTIAL( typename,R, typename,LIST, binder_first) + : public functor_impl +{ + public: + typedef functor outgoing_type; + typedef typename functor::PARM_1 bound_type; + typedef R result_type; + + private: + functor fun_; + bound_type bound_; + public: + COMET_PARTIAL_NAME(binder_first)(const functor& fun, bound_type bound) : bound_(bound) + { + fun_ = fun; + } + + COMET_PARTIAL_NAME(binder_first)(const COMET_PARTIAL_NAME(binder_first)& x) : bound_(x.bound_) + { + fun_ = x.fun_; + } + + functor_impl* clone() const + { return new COMET_PARTIAL_NAME(binder_first)(*this); } + + result_type operator()() + { + return fun_(bound_ ); + } + + result_type operator()( typename outgoing_type::PARM_1 p1) + { + return fun_(bound_, p1); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2) + { + return fun_(bound_, p1, p2); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3) + { + return fun_(bound_, p1, p2, p3); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4) + { + return fun_(bound_, p1, p2, p3, p4); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5) + { + return fun_(bound_, p1, p2, p3, p4, p5); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12, typename outgoing_type::PARM_13 p13) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12, typename outgoing_type::PARM_13 p13, typename outgoing_type::PARM_14 p14) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12, typename outgoing_type::PARM_13 p13, typename outgoing_type::PARM_14 p14, typename outgoing_type::PARM_15 p15) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15); + } + + result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12, typename outgoing_type::PARM_13 p13, typename outgoing_type::PARM_14 p14, typename outgoing_type::PARM_15 p15, typename outgoing_type::PARM_16 p16) + { + return fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16); + } + + +}; +COMET_CLOSE_PARTIAL() + +COMET_SPECIALISE_PARTIAL( void, typename,LIST , binder_first) + : public functor_impl +{ +public: +typedef functor outgoing_type; +typedef typename functor::PARM_1 bound_type; +typedef void result_type; + +private: +functor fun_; +bound_type bound_; +public: +COMET_PARTIAL_NAME(binder_first)(const functor& fun, bound_type bound) : bound_(bound) +{ + fun_ = fun; +} + +COMET_PARTIAL_NAME(binder_first)(const COMET_PARTIAL_NAME(binder_first)& x) : bound_(x.bound_) +{ + fun_ = x.fun_; +} + +functor_impl* clone() const +{ return new COMET_PARTIAL_NAME(binder_first)(*this); } + +result_type operator()() +{ + fun_(bound_ ); +} + +result_type operator()( typename outgoing_type::PARM_1 p1) +{ + fun_(bound_, p1); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2) +{ + fun_(bound_, p1, p2); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3) +{ + fun_(bound_, p1, p2, p3); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4) +{ + fun_(bound_, p1, p2, p3, p4); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5) +{ + fun_(bound_, p1, p2, p3, p4, p5); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12, typename outgoing_type::PARM_13 p13) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12, typename outgoing_type::PARM_13 p13, typename outgoing_type::PARM_14 p14) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12, typename outgoing_type::PARM_13 p13, typename outgoing_type::PARM_14 p14, typename outgoing_type::PARM_15 p15) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15); +} + +result_type operator()( typename outgoing_type::PARM_1 p1, typename outgoing_type::PARM_2 p2, typename outgoing_type::PARM_3 p3, typename outgoing_type::PARM_4 p4, typename outgoing_type::PARM_5 p5, typename outgoing_type::PARM_6 p6, typename outgoing_type::PARM_7 p7, typename outgoing_type::PARM_8 p8, typename outgoing_type::PARM_9 p9, typename outgoing_type::PARM_10 p10, typename outgoing_type::PARM_11 p11, typename outgoing_type::PARM_12 p12, typename outgoing_type::PARM_13 p13, typename outgoing_type::PARM_14 p14, typename outgoing_type::PARM_15 p15, typename outgoing_type::PARM_16 p16) +{ + fun_(bound_, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16); +} + +}; +COMET_CLOSE_PARTIAL() + +/** Constructs a functor that calls a function with an extra parameter (at the + * beginning) of type \p T. This gets called with the value \p bound. + * \relates binder + */ +template +functor bind_first( const functor& fun, const T& bound) +{ + return functor(std::auto_ptr< functor::IMPL >(new binder_firstCOMET_PARTIAL_NS(fun, bound))); +} + +/** Chains \p fun1 to \p fun2. + * \return functor chaining \p fun1 to \p fun2 + * \relates chainer + */ +template +functor chain( const functor& fun1, const functor& fun2) +{ + return functor(std::auto_ptr< functor::IMPL >(new chainer + +#include + +namespace comet { + /*! \addtogroup COMType + */ + //@{ + + class GIT; + + /// Type-safe GIT Cookie. + template class GIT_cookie + { + friend class GIT; + public: + GIT_cookie(const GIT_cookie& c) : cookie_(c.cookie_) {} + explicit GIT_cookie(DWORD c) : cookie_(c) {} + GIT_cookie() : cookie_(0) {} + + DWORD get_cookie() const { return cookie_; } + private: + DWORD cookie_; + }; + + /// Global Interface Table wrapper. + class GIT { + public: + GIT() : git_(CLSID_StdGlobalInterfaceTable) + {} + + /** Register Interface in the GIT. + * \param ptr Interface to register. + * \return Type-safe cookie. + */ + template + GIT_cookie register_interface(com_ptr const & ptr) + { + DWORD cookie; + git_->RegisterInterfaceInGlobal(ptr.get(), uuidof(), &cookie) | raise_exception; + return GIT_cookie(cookie); + } + + /** Retrieve Interface in the GIT. + * \param c Cookie + * \return Marshalled interface. + */ + template + com_ptr get_interface(GIT_cookie const& c) + { + Itf* itf; + git_->GetInterfaceFromGlobal(c.get_cookie(), uuidof(), reinterpret_cast(&itf)) | raise_exception; + return auto_attach(itf); + } + + /** Revoke the cookie from the GIT. + * \param c Cookie. + */ + template + void revoke_interface(GIT_cookie const& c) + { + HRESULT hr = git_->RevokeInterfaceFromGlobal(c.get_cookie()); + hr; + assert(SUCCEEDED(hr)); + } + + private: + com_ptr< ::IGlobalInterfaceTable> git_; + }; + //@} + +} // namespace comet + +#endif diff --git a/lib/local/CamCom/comet/handle.h b/lib/local/CamCom/comet/handle.h new file mode 100644 index 00000000..085bb19e --- /dev/null +++ b/lib/local/CamCom/comet/handle.h @@ -0,0 +1,363 @@ +/** \file +* Wrapper for Win32 API HANDLE. +*/ +/* +* Copyright 2001,2004 Sofus Mortensen, Michael Geddes +* +* This material is provided "as is", with absolutely no warranty +* expressed or implied. Any use is at your own risk. Permission to +* use or copy this software for any purpose is hereby granted without +* fee, provided the above notices are retained on all copies. +* Permission to modify the code and to distribute modified code is +* granted, provided the above notices are retained, and a notice that +* the code was modified is included with the above copyright notice. +* +* This header is part of Comet version 2. +* https://github.com/alamaison/comet +*/ + +#ifndef COMET_HANDLE_H +#define COMET_HANDLE_H + +#include +#include +#include +#include +#include + +#include + +namespace comet { + + /*! \addtogroup COMType + */ + //@{ + + /** Base and policy class for auto_handle_wrap_t type. + * This provides the destroy/detach operations for auto_handle_wrap_t as well as + * providing a base class to contain the handle and to add functions to + * that are specific to the handle type. + * \relates auto_handle_wrap_t + * \sa auto_handle_wrap_t + */ + template< typename H, long INVALID_HANDLE_ > + class handle_policy_base_t + { + public: + /// Default constructor. + handle_policy_base_t() : handle_(reinterpret_cast(INVALID_HANDLE_)) {} + + explicit handle_policy_base_t( H handle) : handle_(handle) {} + + typedef H value_type; + + //! Implicit conversion to HANDLE + operator H() const throw() + { + // This function seemed not to work with returning a namespaced + // type. + return handle_; + } + static inline H invalid_handle() { return reinterpret_cast(INVALID_HANDLE_) ;} + + /// Is the handle valid? + bool valid() const + { + return handle_ != invalid_handle(); + } + + /// Detach a raw handle + H detach_handle() + { + return swap_handle(invalid_handle()); + } + + //! Detaches currently held handle without closing it. + static inline value_type detach( handle_policy_base_t &handle )throw() + { + return handle.detach_handle(); + } + + protected: + /// Destructor to prevent one of these from being created by itself. + ~handle_policy_base_t() + { } + + /// Detach the contained handle to the passed in. + void detach_to(handle_policy_base_t &rhs) throw() + { + value_type handle(handle_); + handle_= invalid_handle(); + rhs.handle_ = handle; + } + + H get_handle() const { return handle_; } + H *get_handle_ptr() { return &handle_;} + + H swap_handle(H new_handle) + { + H old = handle_; + handle_ = new_handle; + return old; + } + + private: + H handle_; + }; + + /** \class handle_nothrow_error_policy_t handle.h comet/handle.h + * Nothrow Error policy. + * \relates auto_handle_wrap_t + */ + struct handle_nothrow_error_policy_t + { + static void on_error() + { } + }; + + /** \class handle_throw_error_policy_t handle.h comet/handle.h + * Throwing Error policy. + * \relates auto_handle_wrap_t + */ + struct handle_throw_error_policy_t + { + static void on_error() + { + DWORD err = GetLastError(); + raise_exception(HRESULT_FROM_WIN32(err)); + } + }; + + /** \class auto_handle_wrap_t handle.h comet/handle.h + * Wrapper for a Win32 API/GDI HANDLE. + * Behaves similarly to an auto_ptr in that it implements single-reference, + * reference counting, with reference-transferring assignment and + * copy-construction. + */ + + template + class auto_handle_wrap_t : public handle_policy_base_t + { + typedef typename handle_policy_base_t::value_type value_type; + protected: + + /// Call destroy_handle + static inline bool destroy_( value_type h) + { + return C_::destroy_handle(h); + } + + /// Destroy the handle passed in. + /** Must be implemented by the parent class. + */ + static bool destroy_handle(value_type h); + + typedef handle_policy_base_t policy_base ; + + void set_handle( value_type h ) throw() + { + destroy_(this->swap_handle( h)); + } + + static bool expect_nonzero( BOOL value) + { + bool is_ok = (value != FALSE); + if (!is_ok) + ERROR_POLICY::on_error(); + return is_ok; + } + public: + + /// default constructor. + auto_handle_wrap_t() throw() + {} + + /// A reference to a handle. + typedef const C_ &ref; + + /** Assign by auto_attach. + */ + auto_handle_wrap_t( const impl::auto_attach_t &handle ) throw() + : policy_base(handle.get()) + {} + + /** Non-const copy constructor - takes control of the reference. + * \param rhs Handle to detach from. + */ + auto_handle_wrap_t( auto_handle_wrap_t &rhs) throw() + { + rhs.detach_to(*this); + } + + /** Non-const copy constructor - takes control of the reference. + * \param rhs Handle to detach from. + */ + auto_handle_wrap_t( policy_base &rhs) throw() + { + rhs.detach_to(*this); + } + + /** Destructor - closes the handle. + */ + ~auto_handle_wrap_t() throw() + { + destroy_(get()); + } + + + //! Assignment from similar handles. + /** Typically, there might be a different error policy. + */ + template + auto_handle_wrap_t & operator=(auto_handle_wrap_t& rhs) throw() + { + close_handle(); + rhs.detach_to(*this); + return *this; + } + + //! Assign by auto_attach + C_ &operator=(const impl::auto_attach_t &handle ) + { + close_handle(); + set_handle(handle.get()); + return *static_cast(this); + } + + //! Closes the currently held handle (if any). + bool close() throw() + { + return destroy_(policy_base::detach(*this)); + } + + //! \name Accessor methods + //@{ + + //! Fitter method. + /*! + Used when calling a function that take a HANDLE* argument. + + \code + auto_handle read_pipe, write_pipe; + + CreatePipe(read_pipe.out(), write_pipe.out(), 0, 0)); + \endcode + */ + typename policy_base::value_type* out() throw() + { + close_handle(); + return this->get_handle_ptr(); + } + + typename policy_base::value_type get() const throw() + { return this->get_handle(); } + typename policy_base::value_type in() const throw() + { return this->get_handle(); } + typename policy_base::value_type *inout() throw() + { return this->get_handle_ptr(); } + //@} + + static inline const C_ &create_const_reference(const value_type &val) + { return *reinterpret_cast(&val); } + + static inline C_ &create_reference(value_type &val) + { return *reinterpret_cast(&val); } + + /// Destroy a reference. + static bool destroy_reference(value_type h) + { + return true; + } + + private: + + //! Closes the currently held handle (if any). + inline void close_handle() + { + if (!close()) + ERROR_POLICY::on_error(); + } + }; + + namespace impl + { + /** \internal + */ + class THIS_IS_NOT_ALLOWED + { + ~THIS_IS_NOT_ALLOWED() {} + }; + } + + /// Disallow closing of a const handle. + template< typename H, long INVALID_HANDLE_ > + inline impl::THIS_IS_NOT_ALLOWED CloseHandle(const handle_policy_base_t&); + + /// Make sure closing of an auto_handle_wrap_t detaches first. + /** \return true if CloseHandle was successful. + */ + template< typename H, long INVALID_HANDLE_ > + bool CloseHandle(handle_policy_base_t &rhs) + { + return rhs.close(); + } + + /// Wrapper for HANDLE type + template< typename ERROR_POLICY = handle_nothrow_error_policy_t > + struct auto_handle_t : auto_handle_wrap_t< auto_handle_t, HANDLE, 0, ERROR_POLICY > + { + typedef auto_handle_wrap_t< auto_handle_t, HANDLE, 0, ERROR_POLICY > handle_base; + + /// Default constructor. + auto_handle_t() {} + /// Copy constructor. + auto_handle_t( auto_handle_t &rhs) + : auto_handle_wrap_t< auto_handle_t, HANDLE, 0, ERROR_POLICY >(rhs) + {} + + /// Auto_attach constructor. + auto_handle_t( const impl::auto_attach_t< HANDLE > &rhs ) + : auto_handle_wrap_t< auto_handle_t, HANDLE, 0, ERROR_POLICY >(rhs) + { } + /// Default assignment. + auto_handle_t &operator=(auto_handle_t &rhs) + { + handle_base::operator=(rhs); + return *this; + } + auto_handle_t &operator=( const impl::auto_attach_t &rhs) + { + handle_base::operator=(rhs); + return *this; + } + + /// Destroy a handle + static bool destroy_handle( HANDLE h) + { + return ::CloseHandle(h) != FALSE; + } + + }; + + /// Auto handle - wrapper for HANLDE. + typedef auto_handle_t<> auto_handle; + /// Auto handle - throwing wrapper for HANDLE. + typedef auto_handle_t< handle_throw_error_policy_t > auto_handle_throw; + + /// Create a reference object to a handle that doesn't destroy it's contents. + template + struct auto_reference_t : T + { + auto_reference_t( const comet::impl::auto_attach_t &rhs ) : T(rhs) + {} + ~auto_reference_t() + { + destroy_reference(this->detach_handle()); + } + }; + //@} +} + +using comet::CloseHandle; + +#endif diff --git a/lib/local/CamCom/comet/handle_except.h b/lib/local/CamCom/comet/handle_except.h new file mode 100644 index 00000000..f5bf6b1c --- /dev/null +++ b/lib/local/CamCom/comet/handle_except.h @@ -0,0 +1,363 @@ +/** \file + * Handle Comet exceptions. + */ +/* + * Copyright 2003 Atex Media Command. All rights reserved. + * Copyright (C) 2013 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ +#ifndef COMET_HANDLE_EXCEPT_H +#define COMET_HANDLE_EXCEPT_H + +#ifdef _SHOW_INC +#pragma message(" #Include " __FILE__) +#endif + +#include + +/** \page comethandleexception Custom Exception Handling + This allows the user to define how exception handling happens for a particular library. + + Unfortunately due to a compiler bug in MSVC 6 (fixed in 7), some of the + functionality won't be available for that compiler (this has also limited + the customisability originally designed for). + + To override the default behavior, specialise the struct + comet::comet_exception_handler and implement the functions defined + in the default implementation comet::comet_exception_handler. + \code + comet { + template<> + struct comet_exception_handler : comet_exception_handler + { + static inline HRESULT catcher_all( const source_info_t &info) + { + throw; + } + }; + } + \endcode + + With a suitable compiler, the method comet::comet_exception_handler::rethrow can be + overridden to provide support for custom exceptions. + + Don't try putting a try catch round a throw in \p catcher_all as the resulting destruct-twice + of the exception when using MSV6 will cause very hard to trace bugs. + */ + +#ifdef COMET_DISABLE_EXCEPTION_RETHROW_CATCH +#define COMET_CATCH_CLASS_EX(funcName,iid) \ + catch ( const com_error &err) { \ + return comet_exception_handler::catcher_com_error(err, impl::do_get_source_info_t<_B>::exec( static_cast<_B*>(this), source_info_t(funcName, iid))); \ + } catch ( const std::exception &err) { \ + return comet_exception_handler::catcher_exception(err, impl::do_get_source_info_t<_B>::exec( static_cast<_B*>(this), source_info_t(funcName, iid))); \ + } catch ( HRESULT hr ) { \ + return comet_exception_handler::catcher_hr(hr, impl::do_get_source_info_t<_B>::exec( static_cast<_B*>(this), source_info_t(funcName, iid))); \ + } catch (...) { \ + return comet_exception_handler::catcher_all( impl::do_get_source_info_t<_B>::exec( static_cast<_B*>(this), source_info_t(funcName, iid))); \ + } + +#define COMET_CATCH_UNKNOWN(funcName, iid, clsname) \ + catch ( const com_error &err) { \ + comet_exception_handler::catcher_com_error(err, source_info_t(funcName, iid, clsname)); \ + } catch ( const std::exception &err) { \ + comet_exception_handler::catcher_exception(err, source_info_t(funcName, iid, clsname)); \ + } catch ( HRESULT hr ) { \ + comet_exception_handler::catcher_hr(hr, source_info_t(funcName, iid, clsname)); \ + } catch (...) { \ + comet_exception_handler::catcher_all( source_info_t(funcName, iid, clsname)); \ + } + + +#else // COMET_DISABLE_EXCEPTION_RETHROW_CATCH + +#define COMET_CATCH_CLASS_EX( funcName, iid) \ + catch ( ... ) { \ + return comet_exception_handler::rethrow( impl::do_get_source_info_t<_B>::exec( static_cast<_B*>(this), source_info_t(funcName, iid))); \ + } + +#define COMET_CATCH_UNKNOWN(funcName, iid, clsname) \ + catch ( ... ) { \ + comet_exception_handler::rethrow( source_info_t(funcName, iid, clsname)); \ + } + +#endif + +/** Macro used in implementation wrappers to hide a bug with catching rethrown + * classes. + */ + +#define COMET_CATCH_CLASS(funcName) COMET_CATCH_CLASS_EX(funcName, comtype::uuid()) + +// We define these new catch macros because the ones above don't seem to work. +// They use a mystery _B to get the class name. It may have been part of the +// old code-generation mechanism which we don't support any more. +#define COMET_CATCH_INTERFACE_BOUNDARY(funcName) \ + catch ( ... ) { \ + return ::comet::comet_exception_handler::rethrow( \ + ::comet::source_info_t( \ + funcName, ::comet::comtype::uuid())); \ + } + +#define COMET_CATCH_CLASS_INTERFACE_BOUNDARY(funcName, clsName) \ + catch ( ... ) { \ + return ::comet::comet_exception_handler::rethrow( \ + ::comet::source_info_t( \ + funcName, ::comet::comtype::uuid(), clsName)); \ + } + +namespace comet { + + /*! \addtogroup ErrorHandling + */ + //@{ + /// Specifies the source of an exception. + struct source_info_t + { + /// Default Constructor + source_info_t() {} + /// Constructor to fill in a few elements. + source_info_t(const bstr_t &par_function, const uuid_t &par_interface_uuid = uuid_t(), const bstr_t &par_coclass_name = bstr_t() ) + : function_name(par_function), interface_uuid(par_interface_uuid), coclass_name(par_coclass_name) {} + + bstr_t function_name; ///< Name of the function being called. + bstr_t coclass_name; ///< Coclass the method belongs to. + uuid_t coclass_uuid; ///< CLSID of the coclass. + uuid_t interface_uuid; ///< IID of the interface + + bstr_t source_override; ///< Used to override the source description. + + /** Returns the 'source' description, either 'source_overrride' or the + * concatenation of the coclass and function_name. + */ + bstr_t source() const + { + if ( !source_override.is_empty()) + { + return source_override; + } + if (coclass_name.is_empty()) + { + return function_name; + } + if (function_name.is_empty()) + { + return coclass_name; + } + return coclass_name + L"." + function_name; + } + + }; + + namespace impl + { + /** Redirect logging to calllog if enabled. + * Used so that if call-logging is loaded, the default action is to + * redirect the logging to the call-logger. + * \internal + */ + template + struct call_logger_redirect_ + { + template + struct exe + { + static inline void log_exception( + const tstring& /*type*/, const tstring& /*desc*/, + const source_info_t& /*errorSource*/, + const source_info_t& /*callSource*/) + { } + static inline bool can_log_exception() + { return false; } + }; + }; + } + + /** \struct error_logger_ handle_except.h comet/handle_except.h + * Default to NULL logger - and description of 'error logger' concept. + * Specialise to \b true to override. + */ + template + struct error_logger_ + { + /** Should the error be logged? + * \return Return true to allow error to be logged. + */ + static inline bool can_log_exception() + { + return impl::call_logger_redirect_::template exe::can_log_exception(); + } + + /// Called by exception handlers to provide logging for errors. + static inline void log_exception(const tstring &type, const tstring &desc, const source_info_t &errorSource ,const source_info_t &callSource ) + { + impl::call_logger_redirect_::template exe::log_exception(type,desc,errorSource, callSource); + } + + }; + + /** Common exception handler for comet. + * Specialize to \c true to overide the behaviour. It is usually convenient + * to inherit the comet_exception_handler from + * comet_exception_handler. + */ + template< bool OVERRIDE> + struct comet_exception_handler + { +#ifndef COMET_DISABLE_EXCEPTION_RETHROW_CATCH + /** Override to modify which exceptions are caught. + * Note that due to a severe bug in MSVC 6, this will not be called in that + * case. + */ + static inline HRESULT rethrow(const source_info_t &info) + { + try { + throw; + } catch ( const com_error &err) + { + return catcher_com_error( err, info); + } + catch ( const std::exception &err) + { + return catcher_exception( err, info); + } + catch ( HRESULT hr ) + { + return catcher_hr( hr, info ); + } + catch (...) + { + return catcher_all(info); + } + + } +#endif // COMET_DISABLE_EXCEPTION_RETHROW_CATCH + + /// Override to modify handling of com_error + static inline HRESULT catcher_com_error( const com_error &err,const source_info_t &info) + { + if ( error_logger_::can_log_exception() ) + { + source_info_t errorInfo; + errorInfo.interface_uuid = err.guid(); + errorInfo.source_override = err.source(); + error_logger_::log_exception( _T("comet::com_error"), err.t_str(), errorInfo, info); + } + return impl::return_com_error(err, info.source(), info.interface_uuid); + } + /// Override to modify handling of std::exception + static inline HRESULT catcher_exception(const std::exception& err,const source_info_t &info) + { + if ( error_logger_::can_log_exception() ) + { + error_logger_::log_exception( _T("std::exception"), + bstr_t(err.what()).t_str(), + source_info_t(), info); + } + return impl::return_com_error(err, info.source(), info.interface_uuid); + } + /// Override to modify handling of HRESULT + static inline HRESULT catcher_hr( HRESULT hr,const source_info_t &info) + { + return impl::return_com_error(hr, bstr_t(), info.source(), info.interface_uuid); + } + /// This can be overridden to provide handling of other exceptions. + static inline HRESULT catcher_all( const source_info_t &info) + { + COMET_NOTUSED(info); + throw; + } + }; + + + namespace impl + { + /** Marker for using the coclass information. + * \relates handle_exception_default + */ + struct handle_exception_default_marker {}; + + /** Call the coclasses exception handler if it is marked. + * \relates handle_exception_default + */ + template + struct do_get_source_info_t + { + /** Call the classes exception handler. + * \relates handle_exception_default + */ + template + struct execute_handle + { + inline static void get_source_info(O * pThis, source_info_t &info) + { + pThis->get_source_info_( info ); + } + }; + /** Call the default global exception handler. + * \relates handle_exception_default + */ + template <> + struct execute_handle + { + inline static void get_source_info(O *pThis, const source_info_t &info ) + { + COMET_NOTUSED(pThis); + COMET_NOTUSED(info); + // return impl::do_handle_exception(src); + } + }; + + /** Called by the interface wrappers to call the exception handlers. + * \relates handle_exception_default + */ + inline static source_info_t exec( O *pThis, source_info_t info) + { + // Either call the coclass exception handler, or the project default. + execute_handle::exists>::get_source_info( pThis, info); + /* ERRORS: If the conversion is ambiguous here, then there are probably two ways of + * getting to handle_exception_default_marker, usually via handle_exception_default, + * which is already provided by by IProvideClassInfoImpl. + */ + return info; + } + }; + } + + /** \struct handle_exception_default server.h comet/server.h + * Used by the exception handler to provide source information for the specified CoClass. + * Inherit from this class or IProvideClassInfoImpl to make sure that the + * coclass name is known about for custom coclasses. + */ + template + struct handle_exception_default + : impl::handle_exception_default_marker // Required to mark the class as being able to handle exceptions with coclass information + { + void get_source_info_( source_info_t &info) + { + info.coclass_name = COCLASS::name(); + info.coclass_uuid = comtype::uuid(); + } + }; + // Handles the case where there is no coclass, and therefore no coclass name. + template<> + struct handle_exception_default : impl::handle_exception_default_marker + { + void get_source_info_( source_info_t &info) + { + COMET_NOTUSED(info); + } + }; + //@} +} +#endif /* COMET_HANDLE_EXCEPT_H */ diff --git a/lib/local/CamCom/comet/impqi.h b/lib/local/CamCom/comet/impqi.h new file mode 100644 index 00000000..67e1f073 --- /dev/null +++ b/lib/local/CamCom/comet/impqi.h @@ -0,0 +1,546 @@ +/** \file + * Implementation of QueryInterface. + */ +/* + * Copyright 2000-2002 Sofus Mortensen, Michael Geddes + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_IMPQI_H +#define COMET_IMPQI_H + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace comet { + /*! \addtogroup Interfaces + */ + //@{ + + + + namespace impl { + + /** Base class for recognising qi hook. + * \internal + */ + class qi_hook_itf_tag {}; + + } + + class qi_hook {}; + + template class qi_hook_itf : public impl::qi_hook_itf_tag{ + public: + typedef Itf exposes; + virtual com_ptr get_interface_ptr(const com_ptr< ::IUnknown>&) throw() = 0; + }; + + namespace impl { + + template COMET_FORCEINLINE bool is_interface_compatible(const uuid_t& iid, Itf*) + { + if (iid == uuidof()) return true; + else return is_interface_compatible::base>(iid, 0); + } + + template<> COMET_FORCEINLINE bool is_interface_compatible< ::IUnknown >(const uuid_t&, ::IUnknown*) + { + return false; + } + + template<> COMET_FORCEINLINE bool is_interface_compatible(const uuid_t&, nil*) + { + return false; + } + enum use_cast_t {uc_false=0, uc_static, uc_static_op, uc_qi_hook_itf, uc_qi_hook }; + + + // remove enum for VC2005B2 + template + struct find_compatibility_aux + { + template + struct with + { + enum { is = false }; + + template inline static bool qi(P *, const uuid_t& , com_ptr< ::IUnknown>& ) + { + return false; + } + }; + }; + + template<> + struct find_compatibility_aux + { + template struct with { + template static bool qi(T *This, const uuid_t& iid, com_ptr< ::IUnknown>& unk) + { + if (is_interface_compatible(iid, 0)) + { + unk = static_cast< ::IUnknown* >(static_cast(This)); + return true; + } + return false; + } + }; + }; + + template<> + struct find_compatibility_aux + { + template struct with { + template static bool qi(T *This, const uuid_t& iid, com_ptr< ::IUnknown>& unk) + { + if (is_interface_compatible(iid,0)) { + unk = static_cast*>(This)->get_interface_ptr( cast_to_unknown(This) ); + return true; + } + return false; + } + }; + }; + + template<> + struct find_compatibility_aux + { + template struct with { + template static bool qi(T *This, const uuid_t& iid, com_ptr< ::IUnknown>& unk) + { + if ( static_cast(This)->qi(This, iid, unk) ) + return true; + else + return false; + } + }; + }; + +/* template<> + struct find_compatibility_aux + { + template + struct with + { + static bool is( const uuid_t &iid){ return is_interface_compatible(iid,0);} + template + static com_ptr< ::IUnknown> cast_from(T *This) + { +#ifndef NDEBUG + try { +#endif + return static_cast*>(This)->get_interface_ptr( cast_to_unknown(This) ); +#ifndef NDEBUG + } catch (...) { + // get_interface_ptr is not allowed to throw. Return null pointer on failure + COMET_ASSERT(0); + return 0; + } +#endif + } + }; + }; + + template<> + struct find_compatibility_aux + { + template + struct with + { + static bool is( const uuid_t &iid){ return true; } + + template + static com_ptr< ::IUnknown> cast_from(T *This) + { + Itf:: + } + }; + };*/ + + template< typename Itf> + struct use_cast_aux + { + enum { is_static = (type_traits::conversion::exists) }; + enum { is_static_op =(type_traits::is_cast_operator_compatible::is)}; + enum { is_qi_hook_itf = (type_traits::conversion::exists) }; + enum { is_qi_hook = (type_traits::conversion::exists) }; + // GCC Doesn't handle evaluation of ?: opeators in templates yet. +// enum { is = (int)( is_static ? uc_static: ( is_static_op ? uc_static_op : uc_false)) + enum { is = is_static * uc_static + + is_qi_hook_itf * uc_qi_hook_itf + + is_qi_hook * uc_qi_hook + + is_static_op * uc_static_op }; + }; + + template + struct find_compatibility + { + enum { needs_cast_ = use_cast_aux::is }; + typedef find_compatibility_aux< (use_cast_t)needs_cast_ > compatible; + + COMET_FORCEINLINE static bool with(const uuid_t &iid) + { return compatible::template with::is(iid); }; + template + COMET_FORCEINLINE static com_ptr< ::IUnknown> cast_from( T *This) + { return compatible::template with::cast_from(This); } + }; + + template struct interface_finder + { + template COMET_FORCEINLINE static bool find_interface(T* This, const uuid_t& iid, com_ptr< ::IUnknown>& rv) + { + typedef typename find_compatibility_aux< (use_cast_t)use_cast_aux< COMET_STRICT_TYPENAME ITF_LIST::head >::is >::template with fc; + if ( fc::qi(This, iid, rv) ) + return true; + else + return interface_finder< COMET_STRICT_TYPENAME ITF_LIST::tail>::find_interface(This, iid, rv); + } + + COMET_FORCEINLINE static bool find_interface_2(const uuid_t& iid) + { + if (is_interface_compatible(iid, 0)) return true; + return interface_finder< COMET_STRICT_TYPENAME ITF_LIST::tail>::find_interface_2(iid); + } + }; + + template<> struct interface_finder + { + template COMET_FORCEINLINE static bool find_interface(T*, const uuid_t&, com_ptr< ::IUnknown>&) + { return false; } + + COMET_FORCEINLINE static bool find_interface_2(const uuid_t&) + { + return false; + } + }; + +/* template<> struct interface_finder > + { + template COMET_FORCEINLINE static ::IUnknown* find_interface(T*, const uuid_t&) + { + return 0; + } + };*/ + + } + + /** \struct typelibrary_loader impqi.h comet/impqi.h + * Type Library Loader. + * Allow provision for different means of loading a type-library. + * \param TL A \e Comet type-library. + */ + template + struct typelibrary_loader + { + //! Load the type-library. + /** Create a different template instantiation of this to load + * type-libraries from a different source (example - from a second + * resource in the dll). + */ + static inline HRESULT load( ITypeLib **pTypeLib) + { return LoadRegTypeLib(uuidof(), TL::major_version, TL::minor_version, LANG_NEUTRAL, pTypeLib); } + }; + + /** \struct implement_qi impqi.h comet/impqi.h + * Implementation of QueryInterface. Inherits from all the types defined + * in \p ITF_LIST. + * \param ITF_LIST interface implementation list. + */ + template class ATL_NO_VTABLE implement_qi : public typelist::inherit_all + { + private: + // Hide qi + void qi(); + public: + /** Get at the unknown for this class. Is here for compatibility when using + * implement_internal_qi via aggregateable_coclass for getting at a + * pointer from which to QueryInterface from. + */ + ::IUnknown* get_unknown()const + { return static_cast< typename ITF_LIST::head * >(const_cast *>(this)); } + + STDMETHOD(QueryInterface)(REFIID riid, void** ppv) + { + const uuid_t& iid = uuid_t::create_const_reference(riid); + com_ptr< ::IUnknown> p; + + impl::interface_finder::find_interface(this, iid, p); + + if (!p) { + if (riid != IID_IUnknown) { + *ppv = 0; + return E_NOINTERFACE; + } + p = get_unknown(); +// p = static_cast< ::IUnknown* >(static_cast< typename ITF_LIST::head * >(this)); + } + + *ppv = reinterpret_cast(p.detach()); + + return S_OK; + } + }; + + /** \struct implement_internal_qi impqi.h comet/impqi.h + * Implementation of QueryInterfaceInternal. Inherits from all the types defined + * in \p ITF_LIST. This implementation is used in aggregation. + * \param ITF_LIST interface implementation list. + */ + template class ATL_NO_VTABLE implement_internal_qi : public typelist::inherit_all + { + private: + void qi(); + public: + /** Get at the unknown for this class. Is especially useful using + * aggregateable_coclass in getting at a pointer from which to + * QueryInterface from. + */ + ::IUnknown* get_unknown()const + { return static_cast< typename ITF_LIST::head * >( const_cast *>(this)); } + + HRESULT QueryInterfaceInternal(REFIID riid, void** ppv) + { + const IID& iid = riid; + com_ptr< ::IUnknown> p; + + impl::interface_finder::find_interface(this, iid, p); + + if (!p) { + if (riid != IID_IUnknown) { + *ppv = 0; + return E_NOINTERFACE; + } +// p = cast_to_unknown(this); + p = static_cast< ::IUnknown* >(static_cast(this)); + } + + *ppv = reinterpret_cast(p.detach()); + + return S_OK; + } + }; + + namespace impl { + template ::IUnknown* cast_to_unknown(implement_qi* iq) + { return static_cast< typename ITF_LIST::head*>(iq); } + } + + /** \class impl_dispatch impqi.h comet/impqi.h + * Implement IDispatch via type-library. + */ + template class ATL_NO_VTABLE impl_dispatch : public BASE + { + protected: + /// \name IDispatch Interface + //@{ + STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo** ti) + { + *ti = get_ti(); + if (*ti) + { + (*ti)->AddRef(); + return S_OK; + } + return E_NOTIMPL; + } + + STDMETHOD(GetTypeInfoCount)(UINT *it) + { *it = 1; return S_OK; } + + STDMETHOD(GetIDsOfNames)(REFIID, OLECHAR** pNames, UINT cNames, LCID, DISPID* pdispids) + { + ITypeInfo* ti = get_ti(); + if (ti) + return ti->GetIDsOfNames(pNames, cNames, pdispids); + else + return E_NOTIMPL; + } + + STDMETHOD(Invoke)(DISPID id, REFIID, LCID, WORD wFlags, DISPPARAMS *pd, VARIANT* pVarResult, EXCEPINFO* pe, UINT* pu) + { + ITypeInfo* ti = get_ti(); + if (ti) + { + void* pThis = static_cast(this); + return ti->Invoke(pThis, id, wFlags, pd, pVarResult, pe, pu); + } + else + return E_NOTIMPL; + } + //@} + private: + ITypeInfo* get_ti() + { + static ITypeInfo* ti_; + if (ti_ == 0) + { + auto_cs lock(module().cs()); + if (ti_ == 0) + { + com_ptr ptl; + + typelibrary_loader::load(ptl.out()); + if (ptl) ptl->GetTypeInfoOfGuid(uuidof(), &ti_); + + if (ti_ != 0) module().add_object_to_dispose( impl::create_itf_releaser(ti_) ); + } + } + return ti_; + } + +// static ITypeInfo* ti_; + }; + + template class com_ptr; + +#if 0 + /** \class aggregates_interface impqi.h comet/impqi.h + * Used as an implementation for an interface to Aggregate the required + * interface. + */ + template< typename Itf > + class aggregates_interface + { + public: + /** Set the inner-unknown returned from the call to CoCreateInstance. + */ + void set_aggregate(const com_ptr< ::IUnknown>& aggobj) { ag_object_ = com_cast(aggobj); } + /** Used by QueryInteface algorithms to work out the interface + * exposed by this class. + */ + typedef Itf exposes; + + operator Itf*() throw() + { + com_ptr< ::IUnknown> test = ag_object_; + return ag_object_.in(); +// return com_ptr( com_cast(ag_object_) ); + } + protected: + com_ptr ag_object_; + + }; +#endif + + + + + + +/* class FTM : public qi_hook_itf + { + private: + com_ptr get_interface_ptr(const com_ptr< ::IUnknown>& This) throw() + { + ::IUnknown* ftm = 0; + CoCreateFreeThreadedMarshaler(This.in(), &ftm); + return com_cast(ftm); + } + };*/ + + /** \struct FTM impqi.h comet/impqi.h + * Aggregate the Free Threaded Marshaller. + * \code + class coclass_implementation + : public coclass + { + ... + }; + \endcode + */ + struct FTM : public qi_hook + { + template + bool qi(T *This, const uuid_t& iid, com_ptr< ::IUnknown>& unk) + { + if (iid != uuidof()) return false; + ::IUnknown* ftm = 0; + CoCreateFreeThreadedMarshaler(impl::cast_to_unknown(This), &ftm); + unk = com_ptr< ::IMarshal>( com_cast(ftm) ); + return unk != 0; + } + }; + + /** \struct aggregates impqi.h comet/impqi.h + * Aggregate an interface. + * \code + class coclass_implementation + : public coclass > + { + coclass_implementation() + { + aggregatescreate_aggregate::create_aggregate(this, CLSCTX_ALL); + } + }; + \endcode + + \sa coclass + */ + template class aggregates : public qi_hook + { + com_ptr< ::IUnknown> inner_; + public: + template bool qi(T *This, const uuid_t& iid, com_ptr< ::IUnknown>& unk) + { + typedef typename make_list::result TL; + if (typelist::length::value > 0) { + if (impl::interface_finder::find_interface_2(iid) == false) return false; + } + if (inner_ == 0) return false; + ::IUnknown* p; + if (SUCCEEDED(inner_.raw()->QueryInterface(iid, reinterpret_cast(&p)))) + { + unk = auto_attach(p); + return true; + } + return false; + } + protected: + template void create_aggregate(T *This, DWORD dwClsContext = CLSCTX_ALL) + { ::IUnknown* unk_this = impl::cast_to_unknown(This); inner_ = com_ptr< ::IUnknown>(uuidof(), com_ptr< ::IUnknown>::create_reference(unk_this), dwClsContext); } + }; + +/* template class aggregates_interface : public qi_hook_itf + { + private: + com_ptr< ::IUnknown> inner_; + com_ptr get_interface_ptr(const com_ptr< ::IUnknown>&) + { return com_cast(inner_); } + protected: + template void create_aggregate(const CLSID& clsid, implement_qi* This, DWORD dwClsContext = CLSCTX_ALL) + { + ::IUnknown* unk_this = static_cast< typename ITF_LIST::head*>(This); + inner_ = com_ptr< ::IUnknown>(clsid, com_ptr< ::IUnknown>::create_reference(unk_this), dwClsContext); + } + + template void create_aggregate(const wchar_t* progid, implement_qi* This, DWORD dwClsContext = CLSCTX_ALL) + { + ::IUnknown* unk_this = static_cast< typename ITF_LIST::head*>(This); + inner_ = com_ptr< ::IUnknown>(progid, com_ptr< ::IUnknown>::create_reference(unk_this), dwClsContext); + } + };*/ + + //@} +} + +#endif diff --git a/lib/local/CamCom/comet/interface.h b/lib/local/CamCom/comet/interface.h new file mode 100644 index 00000000..cfaefb46 --- /dev/null +++ b/lib/local/CamCom/comet/interface.h @@ -0,0 +1,256 @@ +/** \file + * Provide comtype which supplies information about UUIDs & inheritance, + * potentially from Interfaces not defined using a COMET type-library. Also + * defines specialisations for some such standard interfaces. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_INTERFACE_H +#define COMET_INTERFACE_H + +#include + +#include +#include +#include + +#include +#include +#include + +#pragma warning(push, 4) +// NB: coclass_implementation _must_ have no data members. +// The comet framework uses the test +// sizeof coclass_implementation == sizeof coclass_implementation +// in order to determine whether the user has specialized based on T or not. +// The logic here is that any real implementation will have a size that is at least +// sizeof IUnknown, because any real coclass_implementation must at least derive +// off IUnknown and have a "vtable" pointer. +/** \class coclass_implementation interface.h comet/interface.h + * Utility class to make the implementation of a coclass accessible from the + * servers. Implementations of coclasses defined in the type library should + * all be specialisations of this class if they are to be exposed by the + * comet server. + */ +template class coclass_implementation {}; + +#pragma warning(pop) + +namespace comet { + + /** Provide a means for defining new comtype definitions. + */ + template struct uuid_comtype + { + static const uuid_t& uuid() { return uuid_t::create_const_reference(*ItfID); } + typedef BASE base; + }; + + + /** Provide access to uuid and base type of objects. + * Specializations allow access to information relevant to non-comet + * defined interfaces. + */ + template struct comtype { + static const IID& uuid() throw() { return comtype::uuid(); } + typedef typename comtype::base base; + }; + + template<> struct comtype { +// static const IID& uuid() throw() { throw std::logic_error("interface.h:35"); return IID_NULL; } + typedef nil base; + }; + + template<> struct comtype< ::IUnknown > + { + static const IID& uuid() { return IID_IUnknown; } + typedef nil base; + }; + + template<> struct comtype + { + static const IID& uuid() { return IID_IConnectionPoint; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() { return IID_IConnectionPointContainer; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() { return IID_IEnumConnections; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() { return IID_IDispatch; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() { return IID_IEnumVARIANT; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() { return IID_ISupportErrorInfo; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() { return IID_IErrorInfo; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IProvideClassInfo; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IPersist; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IPersistFile; } + typedef ::IPersist base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IPersistStream; } + typedef ::IPersist base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IPersistStreamInit; } + typedef ::IPersist base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IMessageFilter; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IProvideClassInfo2; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IMarshal; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IFontDisp; } + typedef ::IDispatch base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IPictureDisp; } + typedef ::IDispatch base; + }; + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IGlobalInterfaceTable; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IClassFactory; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IStream; } + typedef ::IUnknown base; + }; + + template<> struct comtype + { + static const IID& uuid() throw() { return IID_ISequentialStream; } + typedef ::IUnknown base; + }; + + + + //! C++ replacement of VC's __uuidof() + /*! + Use this function to an IID to an interface or coclass. + */ + template inline const uuid_t& uuidof(Itf * = 0) throw() + { return uuid_t::create_const_reference(comtype::uuid()); } + + namespace impl { + + template struct interface_lookup + { + static bool supports(const uuid_t& iid) + { + if (iid == uuidof()) + return true; + else + return interface_lookup< typename comtype::base >::supports(); + } + + template static Itf* cast(T* t) + { + return static_cast(t); + } + }; + + template<> struct interface_lookup + { + static bool supports(const uuid_t&) + { + return false; + } + + }; + +/* template<> struct interface_lookup > + { + static bool supports(const uuid_t&) + { + return false; + } + + };*/ + } + +} // namespace + +#endif diff --git a/lib/local/CamCom/comet/invariant_lock.h b/lib/local/CamCom/comet/invariant_lock.h new file mode 100644 index 00000000..25215c7b --- /dev/null +++ b/lib/local/CamCom/comet/invariant_lock.h @@ -0,0 +1,126 @@ +/** \file + * Implementation of invariant_lock asserts. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen, Paul Hollingsworth + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_UTIL_H +#define COMET_UTIL_H + +#include + +#include + +#include + +namespace comet { + /*! \addtogroup Misc + */ + //@{ + + /** Contains implementation of invariant_lock asserts. + */ + namespace invariant_lock_impl { + + /** Provides base class for invariant_locks. + * \sa create_invariant_lock enforcer simple_enforcer + */ + class invariant_lock + { + protected: + invariant_lock() {} + invariant_lock(const invariant_lock& /*rhs*/) {} + }; + + /** A version of invariant_lock able to call any void member of a class. + * The member should provide an assertable condition that gets asserted + * on creation and destruction of the lock. + * \sa create_invariant_lock + */ + template struct enforcer : public invariant_lock + { + void (CLASS::*m_passert) () const; + const CLASS *m_pobj; + + enforcer(const CLASS *pobj, void (CLASS::*passert) () const + ) : m_pobj(pobj), m_passert(passert) + { + (m_pobj->*m_passert)(); + } + ~enforcer() + { + (m_pobj->*m_passert)(); + } + }; + + /** A version of invariant_lock that calls public method \b assert_valid on + * construction and destruction of the lock. + * \sa create_invariant_lock + */ + template struct simple_enforcer : public invariant_lock + { + const CLASS *m_pobj; + simple_enforcer(const CLASS *pobj) : m_pobj(pobj) + { + m_pobj->assert_valid(); + } + + ~simple_enforcer() + { + m_pobj->assert_valid(); + } + }; + } // namespace invariant_lock_impl + + /*! Create an invariant_lock. + * \param pobj The class with the invariant assert + * \param assert_member The void member to call on construct & destruct + * \author Paul Hollingsworth (Paul@PaulHollingsworth.com) + * \relates invariant_lock_impl::invariant_lock + * \sa enforcer + */ + template + invariant_lock_impl::enforcer create_invariant_lock(const CLASS *pobj, void (CLASS::*assert_member) () const) + { + return invariant_lock_impl::enforcer(pobj, assert_member); + } + + /*! Create a simple invariant lock. + * This lock will expect assert_valid to be publicly defined on the object + * provided. + * \param pobj The class with the invariant assert. + * \relates invariant_lock_impl::invariant_lock + * \sa simple_enforcer + * \author Paul Hollingsworth (Paul@PaulHollingsworth.com) + */ + template + invariant_lock_impl::simple_enforcer create_invariant_lock(const CLASS *pobj) + { + return pobj; + } + + /*! Pointer class to an invariant_lock. + * Assigning a temporary to a const reference will cause the object to be + * kept for the scope of the const reference. + * \relates invariant_lock_impl::invariant_lock + * \sa create_invariant_lock + * \author Paul Hollingsworth (Paul@PaulHollingsworth.com) + */ + typedef invariant_lock_impl::invariant_lock &invariant_lock; + //@} + +} // namespace + +#endif diff --git a/lib/local/CamCom/comet/lw_lock.h b/lib/local/CamCom/comet/lw_lock.h new file mode 100644 index 00000000..c5752f17 --- /dev/null +++ b/lib/local/CamCom/comet/lw_lock.h @@ -0,0 +1,228 @@ +/** \file + * Lightweight Multiple Reader Single Writer lock. + * + * See \ref cometlwlock. + * + * The lw_lock class is heavily based on class LightweightLock written by Brad Wilson. + * see http://www.quality.nu/dotnetguy/archive/fog0000000007.aspx + * \author Brad Wilson + * \author Sofus Mortensen + */ + +// Copyright (C) 1995-2002 Brad Wilson +// +// This material is provided "as is", with absolutely no warranty +// expressed or implied. Any use is at your own risk. Permission to +// use or copy this software for any purpose is hereby granted without +// fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is +// granted, provided the above notices are retained, and a notice that +// the code was modified is included with the above copyright notice. +// +//////////////////////////////////////////////////////////////////////// + +/** \page cometlwlock Lightweight Lock + This lightweight lock class was adapted from samples and ideas that + were put across the ATL mailing list. It is a non-starving, kernel- + free lock that does not order writer requests. It is optimized for + use with resources that can take multiple simultaneous reads, + particularly when writing is only an occasional task. + + Multiple readers may acquire the lock without any interference with + one another. As soon as a writer requests the lock, additional + readers will spin. When the pre-writer readers have all given up + control of the lock, the writer will obtain it. After the writer + has rescinded control, the additional readers will gain access + to the locked resource. + + This class is very lightweight. It does not use any kernel objects. + It is designed for rapid access to resources without requiring + code to undergo process and ring changes. Because the "spin" + method for this lock is "Sleep(0)", it is a good idea to keep + the lock only long enough for short operations; otherwise, CPU + will be wasted spinning for the lock. You can change the spin + mechanism by #define'ing COMET_LW_LOCK_SPIN before including this + header file. + + VERY VERY IMPORTANT: If you have a lock open with read access and + attempt to get write access as well, you will deadlock! Always + rescind your read access before requesting write access (and, + of course, don't rely on any read information across this). + + This lock works in a single process only. It cannot be used, as is, + for cross-process synchronization. To do that, you should convert + this lock to using a semaphore and mutex, or use shared memory to + avoid kernel objects. + */ + +/* + * Copyright 2002 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#include +#include + +#include + +namespace comet { + +#ifndef COMET_LW_LOCK_SPIN +#define COMET_LW_LOCK_SPIN Sleep(0) +#endif + /*! \addtogroup Misc + */ + //@{ + + /** Provide a lightweight lock imlementation. + * See \ref cometlwlock for more information and warnings. + * \sa auto_reader_lock auto_writer_lock + */ + class lw_lock + { + // Interface + + public: + /// Constructor + lw_lock() + { + reader_count_ = 0; + writer_count_ = 0; + } + + /// Destructor + ~lw_lock() + { + COMET_ASSERT( reader_count_ == 0 ); + COMET_ASSERT( writer_count_ == 0 ); + } + + /// Reader lock acquisition + void enter_reader() const + { + for (;;) + { + // If there's a writer already, spin without unnecessarily + // interlocking the CPUs + + if( writer_count_ != 0 ) + { + COMET_LW_LOCK_SPIN; + continue; + } + + // Add to the readers list + + InterlockedIncrement((long *)&reader_count_ ); + + // Check for writers again (we may have been pre-empted). If + // there are no writers writing or waiting, then we're done. + + if( writer_count_ == 0 ) + break; + + // Remove from the readers list, spin, try again + + InterlockedDecrement((long *)&reader_count_ ); + COMET_LW_LOCK_SPIN; + } + } + + /// Reader lock release + void leave_reader() const + { + InterlockedDecrement((long *)&reader_count_ ); + } + + /// Writer lock acquisition + void enter_writer() + { + // See if we can become the writer (expensive, because it inter- + // locks the CPUs, so writing should be an infrequent process) + + while( InterlockedExchange((long *)&writer_count_, 1 ) == 1 ) + { + COMET_LW_LOCK_SPIN; + } + + // Now we're the writer, but there may be outstanding readers. + // Spin until there aren't any more; new readers will wait now + // that we're the writer. + + while( reader_count_ != 0 ) + { + COMET_LW_LOCK_SPIN; + } + } + + /// Writer lock release + void leave_writer() + { + writer_count_ = 0; + } + + // Implementation + + private: + mutable long volatile reader_count_; + mutable long volatile writer_count_; + + // Declare class non-copyable + lw_lock(const lw_lock&); + lw_lock& operator=(const lw_lock&); + }; + + /** \class auto_reader_lock lw_lock.h comet/lw_lock.h + * Auto-release locking class for lw_lock Read acces. + * \sa lw_lock auto_writer_lock + */ + class auto_reader_lock { + public: + explicit auto_reader_lock(const lw_lock& cs) : cs_(cs) { + cs_.enter_reader(); + } + + ~auto_reader_lock() { + cs_.leave_reader(); + } + + private: + auto_reader_lock& operator=(const auto_reader_lock&); + auto_reader_lock(const auto_reader_lock&); + + const lw_lock& cs_; + }; + + /** \class auto_writer_lock lw_lock.h comet/lw_lock.h + * Auto-release locking class for lw_lock write acces. + * \sa lw_lock auto_reader_lock + */ + class auto_writer_lock { + public: + explicit auto_writer_lock(lw_lock& cs) : cs_(cs) { + cs_.enter_writer(); + } + + ~auto_writer_lock() { + cs_.leave_writer(); + } + + private: + auto_writer_lock& operator=(const auto_writer_lock&); + auto_writer_lock(const auto_writer_lock&); + + lw_lock& cs_; + }; + //@} + +} diff --git a/lib/local/CamCom/comet/module.h b/lib/local/CamCom/comet/module.h new file mode 100644 index 00000000..2bfb8c66 --- /dev/null +++ b/lib/local/CamCom/comet/module.h @@ -0,0 +1,274 @@ +/** \file + * Main functionality for providing a COM module. + */ +/* + * Copyright 2000-2002 Sofus Mortensen, Paul Hollingsworth, Michael Geddes, Mikael Lindgren + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_MODULE_H +#define COMET_MODULE_H + +#include +#include +#include +#include + +namespace comet { + + /*!\addtogroup Server + */ + //@{ + namespace impl { + /** \internal + * 'Command' base class. + */ + struct cmd_t + { + virtual void cmd() = 0; + cmd_t() {} + private: + cmd_t(const cmd_t&); + cmd_t& operator=(const cmd_t&); + }; + + /** \internal + * 'Command' to delete a pointer. + * + */ + template + struct itf_releaser_t : public cmd_t + { + void cmd() { IUnknown *p = p_; p_=NULL; p->Release(); } + itf_releaser_t(T *&p) :p_(p) { } + private: + T *&p_; + itf_releaser_t(const itf_releaser_t&); + itf_releaser_t& operator=(const itf_releaser_t&); + }; + + template + cmd_t *create_itf_releaser( T *&p) + { + return new itf_releaser_t(p); + } + + /** 'Command' to Call object_dispose on an object. + * \internal + */ + template + struct pointer_deleter_t : public cmd_t + { + void cmd() { T *p = p_; p_=NULL; delete p;} + pointer_deleter_t(T *&p) :p_(p) { } + private: + T *&p_; + pointer_deleter_t(const pointer_deleter_t&); + pointer_deleter_t& operator=(const pointer_deleter_t&); + }; + + /** 'Command' to Call object_dispose on an object. + * \internal + */ + template + struct object_disposer_t : public cmd_t + { + void cmd() { p_->object_dispose(); } + object_disposer_t( T*p) : p_(p) {} + private: + T *p_; + object_disposer_t(const object_disposer_t &); + object_disposer_t &operator=(const object_disposer_t &); + }; + } + //! Create a pointer deleter command. + /** A command to delete pointers, commonly used for shutdown. + * \code + module().add_object_to_dispose(create_pointer_deleter(new my_class_t())); + * \endcode + * \sa module_t::add_object_to_dispose + */ + template + impl::cmd_t *create_pointer_deleter( T *&p) + { + return new impl::pointer_deleter_t(p); + } + //! Create an interface releaser command. + /** A command to release COM objects, commonly used for shutdown. + * Used for singletons. + * \code + module().add_object_to_dispose(create_interface_releaser(new my_coclass())); + * \endcode + * \sa module_t::add_object_to_dispose + */ + template + impl::cmd_t *create_interface_releaser( T *&p) + { + return new impl::itf_releaser_t(p); + } + + //! Create a more generic object 'disposer'. + /** Creates a Command that calls a static object_dispose(p) method. + * \code + class my_class_t + { + object_dispose( my_class_t *val) + { + val->destroy_myself(); + } + }; + module().add_object_to_dispose(create_object_disposer(new my_class_t())); + * \endcode + * \sa module_t::add_object_to_dispose + */ + template + impl::cmd_t *create_object_disposer( T *p ) + { + return new impl::object_disposer_t(p); + } + + /// COM module. + struct module_t + { + //! \name Attributes + //@{ + /// Return current reference count. + long rc() + { + return rc_; + } + /// Retun the HINSTANCE of the module. + HINSTANCE instance() const + { + return instance_; + } + + /// Set the hinstance of the module. + void instance(HINSTANCE h) + { + instance_ = h; + } + + /// Return the module's critical_section. + /** \code + auto_cs lock( module().cs() ); + \endcode + */ + const critical_section& cs() const + { + return cs_; + } + + //@} + + //! \name Operations + //@{ + + /// Add to the module locks. + void lock() + { + if (InterlockedIncrement(&rc_) == 1 && shutdown_event_ != 0) + { + auto_cs lock(cs_); + if ( shutdown_event_->is_set()) + shutdown_event_->reset(); + } + } + + /// Decrement the module lock. + void unlock() + { + if(InterlockedDecrement(&rc_)==0) + { + activity_ = true; + if (shutdown_event_ != 0) + { + auto_cs lock(cs_); + shutdown_event_->set(); + } + } + } + + /// Shutdown the server. + void shutdown() + { + for (std::vector::iterator it = objects_to_dispose_.begin(); it != objects_to_dispose_.end(); ++it) + { + (*it)->cmd(); + delete *it; + } + objects_to_dispose_.clear(); + } + + /// Set an event for shutdown. + void set_shutdown_event(event& shutdown_event) + { + shutdown_event_ = &shutdown_event; + } + + /// Remove the event for on shutdown. + void clear_shutdown_event() + { + shutdown_event_ = 0; + } + + /// Returns if there has been activity on the module since last reset. + bool has_activity() const + { + return rc_ != 0 || activity_; + } + + /// Reset the activity marker. + void reset_activity_flag() + { + activity_ = false; + } + + /// Add an objet to be disposed on shutdown. + void add_object_to_dispose(impl::cmd_t* p) + { + auto_cs lock(cs_); + objects_to_dispose_.push_back(p); + } + //@} + + private: + long rc_; + bool activity_; + event* shutdown_event_; + HINSTANCE instance_; + critical_section cs_; + + module_t() : rc_(0), activity_(false), + shutdown_event_(0), instance_(0) + {} + + std::vector objects_to_dispose_; + + friend module_t& module(); + + // declare non-copyable + module_t(const module_t&); + module_t& operator=(const module_t&); + }; + + //! global module object + inline module_t& module() + { + static module_t m; + return m; + } + //@} +} + +#endif diff --git a/lib/local/CamCom/comet/oleidl_comtypes.h b/lib/local/CamCom/comet/oleidl_comtypes.h new file mode 100644 index 00000000..79eec24c --- /dev/null +++ b/lib/local/CamCom/comet/oleidl_comtypes.h @@ -0,0 +1,72 @@ +/** \file + * comtype<> definitions for interfaces in the COM header file OLEIDL.H. + * \author Gabriel Barta + */ +/* + * Copyright 2002 Gabriel Barta + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_OLEIDL_COMTYPES_H +#define COMET_OLEIDL_COMTYPES_H + +#include + +namespace comet { + template<> struct comtype< ::IOleAdviseHolder> + : public uuid_comtype< ::IOleAdviseHolder,&IID_IOleAdviseHolder, ::IUnknown> {} ; + template<> struct comtype< ::IOleCache> + : public uuid_comtype< ::IOleCache,&IID_IOleCache, ::IUnknown> {} ; + template<> struct comtype< ::IOleCache2> + : public uuid_comtype< ::IOleCache2,&IID_IOleCache2, ::IOleCache> {} ; + template<> struct comtype< ::IOleCacheControl> + : public uuid_comtype< ::IOleCacheControl,&IID_IOleCacheControl, ::IUnknown> {} ; + template<> struct comtype< ::IParseDisplayName> + : public uuid_comtype< ::IParseDisplayName,&IID_IParseDisplayName, ::IUnknown> {} ; + template<> struct comtype< ::IOleContainer> + : public uuid_comtype< ::IOleContainer,&IID_IOleContainer, ::IParseDisplayName> {} ; + template<> struct comtype< ::IOleClientSite> + : public uuid_comtype< ::IOleClientSite,&IID_IOleClientSite, ::IUnknown> {} ; + template<> struct comtype< ::IOleObject> + : public uuid_comtype< ::IOleObject,&IID_IOleObject, ::IUnknown> {} ; + template<> struct comtype< ::IOleWindow> + : public uuid_comtype< ::IOleWindow,&IID_IOleWindow, ::IUnknown> {} ; + template<> struct comtype< ::IOleLink> + : public uuid_comtype< ::IOleLink,&IID_IOleLink, ::IUnknown> {} ; + template<> struct comtype< ::IOleItemContainer> + : public uuid_comtype< ::IOleItemContainer,&IID_IOleItemContainer, ::IOleContainer> {} ; + template<> struct comtype< ::IOleInPlaceUIWindow> + : public uuid_comtype< ::IOleInPlaceUIWindow,&IID_IOleInPlaceUIWindow, ::IOleWindow> {} ; + template<> struct comtype< ::IOleInPlaceActiveObject> + : public uuid_comtype< ::IOleInPlaceActiveObject,&IID_IOleInPlaceActiveObject, ::IOleWindow> {} ; + template<> struct comtype< ::IOleInPlaceFrame> + : public uuid_comtype< ::IOleInPlaceFrame,&IID_IOleInPlaceFrame, ::IOleInPlaceUIWindow> {} ; + template<> struct comtype< ::IOleInPlaceObject> + : public uuid_comtype< ::IOleInPlaceObject,&IID_IOleInPlaceObject, ::IOleWindow> {} ; + template<> struct comtype< ::IOleInPlaceSite> + : public uuid_comtype< ::IOleInPlaceSite,&IID_IOleInPlaceSite, ::IOleWindow> {} ; + template<> struct comtype< ::IContinue> + : public uuid_comtype< ::IContinue,&IID_IContinue, ::IUnknown> {} ; + template<> struct comtype< ::IViewObject> + : public uuid_comtype< ::IViewObject,&IID_IViewObject, ::IUnknown> {} ; + template<> struct comtype< ::IViewObject2> + : public uuid_comtype< ::IViewObject2,&IID_IViewObject2, ::IViewObject> {} ; + template<> struct comtype< ::IDropSource> + : public uuid_comtype< ::IDropSource,&IID_IDropSource, ::IUnknown> {} ; + template<> struct comtype< ::IDropTarget> + : public uuid_comtype< ::IDropTarget,&IID_IDropTarget, ::IUnknown> {} ; + template<> struct comtype< ::IEnumOLEVERB> + : public uuid_comtype< ::IEnumOLEVERB,&IID_IEnumOLEVERB, ::IUnknown> {} ; +}; + +#endif //COMET_OLEIDL_COMTYPES_H diff --git a/lib/local/CamCom/comet/ptr.h b/lib/local/CamCom/comet/ptr.h new file mode 100644 index 00000000..3e101a66 --- /dev/null +++ b/lib/local/CamCom/comet/ptr.h @@ -0,0 +1,1261 @@ +/** \file + * Provides the com_ptr type. + */ +/* + * Copyright 2000-2002 Sofus Mortensen, Michael Geddes + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ +/*! \addtogroup COMType + */ +//@{ + +/** \page cometcomptr Comet com_ptr + * \section cometcomptroverview Overview + * The \link comet::com_ptr com_ptr \endlink type is essentially a reference + * counting wrapper for objects that support AddRef / Release in a way that + * is STL container compatible. + * + * It also provides a mechanism for doing casting (QueryInterface) assignments + * as well as for doing assignment-compatible assignments and for providing + * smart wrappers for the interfaces. + * + * \section cometcomptrwrapping What Can Be Wrapped? + * + * The com_ptr has been designed to allow most classes to be wrapped, specifically + * it allows for wrapping ::IUnknown based interfaces, and also coclass implementations. + * + * It should be noted that some coclasses can have two implementations of + * ::IUnknown (aggregateable coclasses being a prime example). These classes + * support \link comet::implement_qi::get_unknown get_unknown() \endlink which allows + * the com_ptr to get a the unknown responsible for the lifetime management of + * the object. + * + * \section cometcomptrassign Assigning Pointers + * + * There are three ways of assigning and constructing com_ptr objects, depending on your + * objective; direct assignment and the two cast operators com_cast and try_cast. + * + * \subsection cometcomptrassigndirect Direct Assignment + + * The first way is trivial, but quite important and is the simple + * assignment of one com_ptr to another. This is done in such a way as to make + * it possible to assign assignment compatible interfaces (eg an ::IUnknown pointer can be + * assigned from an ::IDispatch pointer). A compile-time error will occur if + * the pointers are not compatible. + * + * Unfortunately there are certain circumstances that MSVC6 compiler does not + * provide a complete enough template instantiation backtrace in order to be + * able to directly work out an offending assignment. Our appologies, but a + * decent compiler would be more accomodating! + * + * \subsection cometcomptrassignquery Casting Assignment + * + * To cause a COM cast or ::QueryInterface to happen, you need to use the + * wrapper functions com_cast and try_cast. The cause a QueryInterface on + * both assignment and construction, allowing you the choice between having + * an exception thrown on failure (try_cast) or to silently fail leaving the + * object NULL (com_cast). + * + * \code + + com_ptr viewobj = com_cast( obj ); // <-- non-throwing query-interface + if (viewobj.is_null()) + { + com_ptr viewobj2 = try_cast( obj ); // <-- throwing query-interface + viewobj = viewobj2; // <-- Assignment between assignment compatible types. + } + * \endcode + + * This shows the three different ways of assigning. The first uses com_cast + * that causes the assignment to do a QueryInterface, but does not throw an + * error. The second uses try_cast that again causes the assignment to + * do a QueryInterface, but causes errors to be thrown. The third assignment is + * for assignment compatible objects only, and will cause a compiler-error + * unless (in this case) IViewObject2 inherits off IViewObject (which it does).

+ * + * \subsection cometcomptrassignvariant Assignment From a variant_t + * + * Assignment to a com_ptr from a variant_t must be done by either com_cast or + * try_cast, as any assignment from a variant is effectively a cast. + * + * \section cometcomptrsmartwrapper Smart Wrappers + * + * The com_ptr \link comet::com_ptr::operator-> operator-> \endlink provides wrapped + * access to the methods on the interface. For interfaces that have been + * generated by tlb2h, the interface returned is a wrapper that allows access + * only to the wrapped methods, otherwise, access to the raw interface is + * default. + * + * The design of the wrappers allows for system interfaces to be wrapped as + * well. There is an \link comet::com_ptr< ::IDispatch> IDispatch wrapper \endlink + * defined in comet/dispatch.h that provides methods to call functions and + * property accessors by name or by dispatch id. + * + * There is also a wrapper for the API TypeLibrary information interfaces + * defined in comet/tlbinfo.h. In addition to providing some simple wrappers + * to hide the raw COM types, it also provides accessor wrappers to the structs + * that are allocated/deallocated by the interfaces. + * + * \subsection cometcomptrsmartwrapperdetail Implementation + * It is not necessary to know how the wrappers are implemented in order + * to use them, however the technique is quite interesting. + * + * They key to the wrappers is template specialisation. The operator-> + * returns the 'this' pointer with a reinterpret_cast to a wrap_t + * struct instantiated to the interface contained by the com_ptr. The default + * wrap_t template definition is to inherit from the interface, thus giving + * direct access to methods. + * + * The tlb2h generated headers (as well as comet/tlbinfo.h and + * a0omet/dispatch.h) provide alternate specialisations for wrap_t that wrap the + * arguments and call the real methods (which would be Invoke if it is an dispinterface) + * by using reinterpret_cast to cast back to the original interface. + * + * \section cometcomptraccess Raw COM Access + * Whether calling raw COM interfaces, or trying to understand how the wrappers + * work, you will come across the \ref cometrawcomaccess methods. These are standard + * across the types (see \ref cometrawcomaccess), however com_ptr classes also + * have a raw() method that is equivalent to get() and proivdes access to the + * raw COM methods. + */ + //@} + +#ifndef COMET_PTR_H +#define COMET_PTR_H + +#include + +#include + +#include +#include +#include +#include +#include + +// Primary namespace for comet +namespace comet { + class uuid_t; + + template struct wrap_t : public Itf {}; + + template<> struct wrap_t< ::IUnknown> {}; + + template Itf* raw( wrap_t* p ) { return reinterpret_cast(p); } + +// template struct prop_wrapper : public Itf {}; + + // Forward declaration + class variant_t; + template class com_ptr; + class identity_ptr; + namespace thread_model{ + enum thread_model_t; + } + template struct coclass; + template struct aggregate_inner_unknown ; + + // comet implementation details + namespace impl { + + template class com_cast_t { + public: + explicit com_cast_t(Itf* p) : ptr_(p) {}; + Itf* get() const { return ptr_; } + private: + Itf* ptr_; + com_cast_t& operator=(const com_cast_t&); + }; + + template<> class com_cast_t { + public: + explicit com_cast_t(const variant_t& v) : val_(v) {}; + const variant_t& get() const { return val_; } + private: + const variant_t& val_; + com_cast_t& operator=(const com_cast_t&); + }; + + /** Allow calling specific, known ambiguous IUnknown implementations. + * \param B \b true Call via get_unknown. \b false Call directly + */ + template + struct IUnknown_caller + { + template< typename T > static inline long AddRef( T *y) + { return y->AddRef(); } + template static inline long Release( T *y) + { return y->Release(); } + template static inline HRESULT QueryInterface( T *y, REFIID iid, void **NewIface) + { return y->QueryInterface(iid, (void **)NewIface); } + }; + + template <> + struct IUnknown_caller + { + template static inline long AddRef( T *y) + { return y->get_unknown()->AddRef(); } + template< typename T> static inline long Release( T *y) + { return y->get_unknown()->Release(); } + template static inline HRESULT QueryInterface( T *y, REFIID iid, void **NewIface) + { return y->get_unknown()->QueryInterface(iid, NewIface); } + }; +// template struct implement_internal_qi; + + /** Namespace for dummy functions for allowing IUnknown choice to be + * deferred to the get_unknown() function. + */ + namespace unknown_choice + { +// template long dummy_( coclass *); +// template long dummy_(implement_internal_qi *); + template long dummy_(aggregate_inner_unknown *); + char dummy_( ... ); + } + + /** \internal + */ + template + struct iunknown_chooser + { + + enum { matches = (sizeof( unknown_choice::dummy_( static_cast< C *>(0) ))==sizeof(long)) }; + }; + + } + /*! \addtogroup COMType + */ + //@{ + + //! Cast com_ptr. + /*! Allows QueryInterface when casting to different com_ptr type. + \code + com_ptr foo; + com_ptr bar; + bar = com_cast(foo); + if (!bar.is_null()) { + // Cast is ok. + bar->DoTheThing(); + } + \endcode + * \param t com_ptr to cast + * \relates com_ptr + */ + template inline impl::com_cast_t com_cast(const com_ptr& t) { return impl::com_cast_t(t.get()); } + template inline impl::com_cast_t com_cast(Itf* t) { return impl::com_cast_t(t); } + inline impl::com_cast_t com_cast(const variant_t& v) { return impl::com_cast_t(v); } + + namespace impl { + template class try_cast_t { + public: + explicit try_cast_t(Itf* p) : ptr_(p) {}; + Itf* get() const { return ptr_; } + private: + Itf* ptr_; + try_cast_t& operator=(const try_cast_t&); + }; + + template<> class try_cast_t { + public: + explicit try_cast_t(const variant_t& v) : val_(v) {}; + const variant_t& get() const { return val_; } + private: + const variant_t& val_; + try_cast_t& operator=(const try_cast_t&); + }; + } + + + namespace impl { + + //! IUnknown wrapper. + /** Hides the members of IUnknown. + * \relates com_ptr + */ + template class safe_interface : public Itf { + private: + HRESULT __stdcall QueryInterface(REFIID, void**); + ULONG __stdcall AddRef(); + ULONG __stdcall Release(); + }; + + } + + //! Represents the identity Unknown of an object. + /** Is the only really efficient and safe way of representing an object for + * comparisons. + */ + class identity_ptr + { + public: + //! Default constructor. + /** Initialises the pointer to Null. + */ + identity_ptr() throw() : ptr_(NULL){} + //! Destructor. + /** Releases the pointer. + */ + ~identity_ptr() throw() { release();} + + //! Copy constructor. + /*! Simple pointer copy. They are both already identity unknowns. + */ + identity_ptr( const identity_ptr &rhs) : ptr_(rhs.ptr_) {addref();} + + //! Constructor from Try Cast. + /*! Used for construction from any Interface pointer. Always QIs to + * guarantee this is the identity. + */ + template + explicit identity_ptr( const impl::try_cast_t &x ) throw(com_error) + : ptr_(NULL) + { + IUnknown *p=static_cast(x.get()); + if (p != NULL) + p->QueryInterface(uuidof< ::IUnknown >(), reinterpret_cast(&ptr_)) | raise_exception; + } + + //! Constructor from variant_t Try Cast. + /*! Used for construction from a variant. Always QIs to + * guarantee this is the identity. + */ + inline explicit identity_ptr(const impl::try_cast_t& v) throw(com_error); + + //! Constructor from Com Cast. + /*! Used for construction from any Interface pointer. Always QIs to + * guarantee this is the identity. + */ + template + explicit identity_ptr( const impl::com_cast_t &x ) throw(com_error) + : ptr_(NULL) + { + IUnknown *p=static_cast(x.get()); + if (p != NULL) + p->QueryInterface(uuidof< ::IUnknown >(), reinterpret_cast(&ptr_)); + } + //! Constructor from variant_t com_cast. + /*! Used for construction from a variant. Always QIs to + * guarantee this is the identity. + */ + inline explicit identity_ptr(const impl::com_cast_t& v) throw(com_error); + + //! Constructions of null pointer + /*! + \param null + Only 0 is valid. Any other value will cause E_POINTER to be thrown. + + \exception com_error + Throws E_POINTER if a non-zero value is specified. + */ + explicit identity_ptr(int null) throw(com_error) + : ptr_(NULL) + { if(null != 0) raise_exception(E_POINTER); } + + //! Copy assignment. + /*! Don't QI as this is definitely identity already. + */ + identity_ptr &operator =( const identity_ptr &rhs) throw() + { + identity_ptr tmp(rhs); + swap(tmp); + return *this; + } + + //! Other assignment. + /*! Always QIs. Handles other assignments that have + * available constructors. + */ + template + identity_ptr &operator =(const T &rhs) throw(com_error) + { + identity_ptr tmp(rhs); + swap(tmp); + return *this; + } + //! Null assignment + /*! + Only null is allowed as argument. Attempting to assign a non-zero value will result in E_POINTER (wrapped in com_error) being thrown. + + \exception com_error + Throw E_POINTER if a non-zero value is specified. + */ + identity_ptr& operator=(int null) throw(com_error) + { if(null != 0) raise_exception(E_POINTER); release(); return *this; } + + /// Get at the raw pointer. + IUnknown *get() const throw() { return ptr_; } + /// Pass to an [in] parameter. + IUnknown *in() const throw() { return ptr_; } + /// Get at raw interface. + IUnknown *raw() const throw() { return ptr_; } + + //! Returns true if the wrapped pointer is null. + bool is_null() const throw() + { return (ptr_ ==NULL); } + + //! Null comparison + /*! + Only comparison with a value of zero is allowed. Non-zero values will result + in E_POINTER (wrapped in com_error) being thrown. + + \exception com_error + */ + bool operator==(int null) const + { if (null != 0) raise_exception(E_POINTER); return is_null(); } + //! Null comparison + /*! + Only comparison with a value of zero is allowed. Non-zero values will result + in E_POINTER (wrapped in com_error) being thrown. + + \exception com_error + */ + bool operator!=(int null) const + { if (null != 0) raise_exception(E_POINTER); return !is_null(); } + + /**! \name Pointer Comparison + */ + //@{ + bool operator<(const identity_ptr& x) const throw() + { return ptr_ < x.ptr_; } + + bool operator>(const identity_ptr& x) const throw() + { return ptr_ > x.ptr_; } + + + bool operator<=(const identity_ptr& x) const throw() + { return ptr_ <= x.ptr_; } + + bool operator>=(const identity_ptr& x) const throw() + { return ptr_ >= x.ptr_; } + + bool operator==(const identity_ptr& x) const throw() + { return (ptr_ == x.ptr_); } + + bool operator!=(const identity_ptr& x) const throw() + { return ptr_ != x.ptr_; } + //@} + + protected: + IUnknown *ptr_; + + void swap(identity_ptr& x) throw() + { std::swap(ptr_, x.ptr_); } + + void addref() const throw() + { if (ptr_ != NULL) ptr_->AddRef(); } + + void release() throw() + { if (ptr_ != NULL) { ptr_->Release(); ptr_ = NULL; } } + + private: + // Can't use an identity_ptr for out, inout, use com_ptr + IUnknown **inout(); + IUnknown *out(); + + }; + + //! Interface smart pointer + /*! \note All members are exception safe. + * \sa cometcomptr + */ + template class com_ptr + { + public: + const uuid_t& iid() const throw() + { return uuidof(); } + + private: + enum{ chooser_matches_= impl::iunknown_chooser::matches }; + typedef typename impl::IUnknown_caller< chooser_matches_ > Unknown_caller_; + + void addref() const throw() + { if (ptr_) Unknown_caller_::AddRef(ptr_); } + + void release() throw() + { if (ptr_) { Unknown_caller_::Release(ptr_); ptr_ = 0; } } + + public: + //! Type of pointer to wrapped interface + typedef Itf* interface_pointer; + + //! Safe interface pointer. + /*! + Interface pointer where the methods from IUnknown have been hidden. + */ +#ifndef COMET_USE_RAW_WRAPPERS + typedef wrap_t* safe_interface_pointer; +#else + typedef impl::safe_interface< Itf >* safe_interface_pointer; +#endif + + private: + interface_pointer ptr_; + + public: + //! Default constructor + /*! + Initialises the wrapped pointer to null. + */ + com_ptr() throw() : ptr_(NULL) {} + + //! Destructor + /*! + Calls Release() on pointer if necessary. + */ + ~com_ptr() throw() + { release(); } + + //! Upcasting constructor + /*! + Used for upcasting interface pointer without invocation of QueryInterface. + + This contructor only allows com_ptr upcasts. + + \param x + com_ptr to cast. + */ + + // If you are getting: + // error C2440: '=' : cannot convert from 'struct ... + // + // Use either try_cast or com_cast to cast from one interface to another + // example: + // + // com_ptr p = try_cast( q ); + // + // or + // + // com_ptr p = com_cast( q ); + template com_ptr(const com_ptr& x) throw() + { ptr_ = (Itf2*)x.get(); addref(); } + + //! QueryInterface construction + /*! + Uses QueryInterface to query for interface of type Itf. If the pointer is incompatible, the pointer will be initialised to null. + + \code + com_ptr foo; + com_ptr bar( com_cast(foo) ); + \endcode + + \param x + com_ptr to cast + */ + template com_ptr(const impl::com_cast_t& x) throw() + { ptr_ = NULL; Itf2* p = x.get(); if (p != NULL) Unknown_caller_::QueryInterface(p, iid(), reinterpret_cast(&ptr_)); } + // ^ Note: no error checking!!! + + //! QueryInterface construction + /*! + Uses QueryInterface to query for interface of type Itf. Should the pointer be incompatible, com_error will be thrown. + + \code + com_ptr foo = Foo::create(); + com_ptr bar( try_cast(foo) ); + \endcode + + \param x + com_ptr to cast + + \exception com_error + Throws E_NOINTERFACE if cast fails. + Throws E_POINTER if pointer is zero. + */ + template com_ptr(const impl::try_cast_t& x) throw(com_error) + : ptr_(NULL) + { + Itf2* p = x.get(); if (p == NULL) return; + Unknown_caller_::QueryInterface(p, iid(), reinterpret_cast(&ptr_)) | raise_exception; + if (ptr_ == NULL) raise_exception(E_NOINTERFACE); + } + + //! Copy constructor + /*! + \param x com_ptr to copy. + */ + com_ptr(const com_ptr& x) throw() + : ptr_(x.ptr_) + { addref(); } + + //! Copy from identity_unknown class. + /** Itf must be IUnknown for this to work. Otherwise use try_cast or + * com_cast. + */ + com_ptr( const identity_ptr &x) throw() + : ptr_(x.ptr_) + { addref(); } + + private: + // If you are getting: + // error C2248: '=' : cannot access private member ... + // + // Use either try_cast or com_cast to convert a variant_t to a com_ptr. + // example: + // + // com_ptr p = try_cast( v ); + // + // or + // + // com_ptr p = com_cast( v ); + com_ptr(const variant_t&); + com_ptr& operator=(const variant_t&); + + public: + + //! Construction from variant_t + /*! + This constructor does not throw. The wrapped pointer is initialised to null if the variant is incompatible. + + \param v + Wrapped com_variant to construct com_ptr from + */ + com_ptr(const impl::com_cast_t& v) throw() + { create_nothrow(v.get()); } + + //! Construction from variant_t + /*! + Throws com_error should the variant be incompatible. + + \param v + Wrapped com_variant to construct com_ptr from + + \exception com_error + Throws E_NOINTERFACE if cast fails. + */ + com_ptr(const impl::try_cast_t& v) throw(com_error) + { create(v.get()); } + + //! Construction from a raw interface pointer + /*! + Calls AddRef, and thus does \em not take ownership of the pointer. + + \param p + Interface pointer to construct com_ptr from. + */ + com_ptr(interface_pointer p) throw() + : ptr_(p) + { addref(); } + + //! Attach construction of raw interface pointer + /*! + An attachment construction does not AddRef on the interface, and is said + to take ownership of the interface pointer. + + \param p + auto_attach wrapped pointer to construct com_ptr from. + */ + com_ptr(const impl::auto_attach_t& p) throw() + : ptr_( p.get() ) + {} + + //! Construction from CLSID + /*! + \param clsid + Class ID of coclass. + \param dwClsContext + Class context to create object in. + + \exception com_error + */ + explicit com_ptr(const uuid_t& clsid, DWORD dwClsContext = CLSCTX_ALL) throw(com_error) + { create(clsid, NULL, dwClsContext); } + + //! Construction of aggregated object from CLSID + /*! + \param clsid + Class ID of coclass. + \param outer + Pointer to outer object. + \param dwClsContext + Class context to create object in. + + \exception com_error + */ + /*template*/ com_ptr(const uuid_t& clsid, const com_ptr< ::IUnknown>& outer, DWORD dwClsContext = CLSCTX_ALL) throw(com_error) + { create(clsid, outer, dwClsContext); } + + //! Constructions of null pointer + /*! + \param null + Only 0 is valid. Any other value will cause E_POINTER to be thrown. + + \exception com_error + Throws E_POINTER if a non-zero value is specified. + */ + explicit com_ptr(int null) throw(com_error) + : ptr_(NULL) + { if(null != 0) raise_exception(E_POINTER); } + + //! Construction from ProgID + /*! + \param progid + ProgID of class. + \param dwClsContext + Class context to create object in. + + \exception com_error + */ + explicit com_ptr(const wchar_t* progid, DWORD dwClsContext = CLSCTX_ALL) throw(com_error) + { create(progid, 0, dwClsContext); } + + //! Construction from ProgID + /*! + \param progid + ProgID of class. + \param outer + Pointer to outer object. + \param dwClsContext + Class context to create object in. + + \exception com_error + */ + /*template */com_ptr(const wchar_t* progid, const com_ptr< ::IUnknown>& outer, DWORD dwClsContext = CLSCTX_ALL) throw(com_error) + { create(progid, outer, dwClsContext); } + + //! Construction using CoGetObject + explicit com_ptr(const wchar_t* name, BIND_OPTS& bind_opts) + { create(name, bind_opts); } + + //! Arrow operator + /*! + Used to access methods and properties of the wrapped interface. + + \note The methods of IUnknown are deliberately hidden. + */ + safe_interface_pointer operator->() const throw(com_error) + { if( ptr_ == NULL) raise_exception(E_POINTER); return get_safe_ptr(); } + + //! Assignment of raw interface pointer + com_ptr& operator=(interface_pointer x) throw() + { com_ptr t(x); swap(t); return *this; } + + //! QueryInterface assignment + /*! + In order to cast unrelated interfaces you must use either com_cast or try_cast. + + A com_cast that fails results in a null pointer assignment. + + \code + com_ptr foo; + com_ptr bar; + bar = com_cast(foo); + if (!bar.is_null()) { + // Cast is ok. + bar->DoTheThing(); + } + \endcode + */ + template com_ptr& operator=(const impl::com_cast_t& x) throw() + { com_ptr t(x); swap(t); return *this; } + + //! QueryInterface assignment + /*! + In order to cast unrelated interfaces you must use either com_cast or try_cast. + + A try_cast that fails will throw E_NOINTERFACE wrapped in com_error. + + \code + com_ptr foo; + com_ptr bar; + try { + bar = try_cast(foo); + bar->DoTheThing(); + } catch (com_error&) { + // Cast didn't work. + } + \endcode + + \exception com_error + Throws E_NOINTERFACE if cast fails. + */ + template com_ptr& operator=(const impl::try_cast_t& x) throw(com_error) + { com_ptr t(x); swap(t); return *this; } + + template + com_ptr& operator=(const com_ptr& x) throw() + { com_ptr t(x); swap(t); return *this; } + + //! Default assigment operator. + com_ptr& operator=(const com_ptr& x) throw() + { com_ptr t(x); swap(t); return *this; } + + //! Null assignment + /*! + Only null is allowed as argument. Attempting to assign a non-zero value will result in E_POINTER (wrapped in com_error) being thrown. + + \exception com_error + Throw E_POINTER if a non-zero value is specified. + */ + com_ptr& operator=(int null) throw(com_error) + { if(null != 0) raise_exception(E_POINTER); release(); return *this; } + + //! Attaching assignment + /*! + Attaches a raw interface pointer to the com_ptr. + + \code + com_ptr foo; + foo = auto_attach( raw_foo_pointer ); + \endcode + */ + com_ptr& operator=(const impl::auto_attach_t& p) throw() + { release(); ptr_ = p.get(); return *this; } + + private: + template + int compare_unknown(const com_ptr& x) const throw(com_error) + { + if ( (void *)ptr_ == (void *)x.get()) return 0; + ::IUnknown* p1 = 0; + ::IUnknown* p2 = 0; + + if (ptr_ != NULL) { + Unknown_caller_::QueryInterface(ptr_,uuidof< ::IUnknown >(), reinterpret_cast(&p1)) | raise_exception; + p1->Release(); + } + + if (x.get()) { + Unknown_caller_::QueryInterface(x.get(), uuidof< ::IUnknown >(), reinterpret_cast(&p2)) | raise_exception; + p2->Release(); + } + + return p1 - p2; + } + int compare_unknown(const identity_ptr& x) const throw(com_error) + { + if (ptr_ == x.get()) return 0; + ::IUnknown* p1 = 0; + ::IUnknown* p2 = 0; + + if (ptr_ != NULL) { + Unknown_caller_::QueryInterface(x.get(), uuidof< ::IUnknown >(), reinterpret_cast(&p2)) | raise_exception; + p2->Release(); + } + + return p1 - p2; + } + public: + /**! \name Pointer Comparison + */ + //@{ + template bool operator<(const T& x) const throw(com_error) + { return compare_unknown(x) < 0; } + + template bool operator>(const T& x) const throw(com_error) + { return compare_unknown(x) > 0; } + + template bool operator<=(const T& x) const throw(com_error) + { return compare_unknown(x) <= 0; } + + template bool operator>=(const T& x) const throw(com_error) + { return compare_unknown(x) >= 0; } + + /** Object equality. + */ + template bool operator==(const com_ptr& x) const throw(com_error) + { return compare_unknown(x) == 0; } + + template bool operator!=(const com_ptr& x) const throw(com_error) + { return compare_unknown(x) != 0; } + + //@} + + //! Returns true if the wrapped pointer is null. + bool is_null() const throw() + { return (ptr_ ==NULL); } + + //! Returns true if the wrapped pointer is null. + bool operator!() const throw() + { return is_null(); } + + //! Null comparison + /*! + Only comparison with a value of zero is allowed. Non-zero values will result + in E_POINTER (wrapped in com_error) being thrown. + + \exception com_error + */ + bool operator==(int null) const + { if (null != 0) raise_exception(E_POINTER); return is_null(); } + + //! Null comparison + /*! + Only comparison with a value of zero is allowed. Non-zero values will result + in E_POINTER (wrapped in com_error) being thrown. + + \exception com_error + */ + bool operator!=(int null) const + { if (null != 0) raise_exception(E_POINTER); return !is_null(); } + + //! Pointer comparison + /*! + Returns true if the two pointers are the same. + */ + template inline bool same_pointer( const com_ptr &x) const throw(com_error) + { return (IUnknown *) ptr_ == (IUnknown *)x.ptr_; } + + //! Swap operation + /*! + This method is very fast, since it does not call AddRef or Release. + */ + void swap(com_ptr& x) throw() + { std::swap(ptr_, x.ptr_); } + + //! Detaches ownership. + /*! + Detaches the pointer from the wrapper and returns it as raw pointer. + + This method is primarily for use by the interface wrapper code. You should very seldomly find use for it. + */ + interface_pointer detach() throw() + { interface_pointer t = ptr_; ptr_ = 0; return t; } + + //! Detaches ownership + /*! + */ + static interface_pointer detach(com_ptr& x) throw() + { return x.detach(); } + + /**! Create a const reference to a pointer without invoking reference-counting. + * Since neither constructor or destructor get called. + */ + static const com_ptr& create_const_reference(const interface_pointer& x) + { return *reinterpret_cast(&x); } + + /**! Create a reference to a pointer without invoking reference-counting. + * Since neither constructor or destructor get called. + */ + static com_ptr& create_reference(interface_pointer& x) + { return *reinterpret_cast(&x); } + + //! \name Adapter methods. + //@{ + + //! [in] adapter + /*! + Used when calling raw interfaces that require an [in] IXXX * argument. + + \code + com_ptr foo; + HRESULT hr = pRawInterface->raw_Method(foo.in()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + interface_pointer in() const throw() + { return ptr_; } + + interface_pointer raw() const throw() + { return ptr_; } + + interface_pointer get() const throw() + { return ptr_; } + + //! [out] adapter. + /*! + Used when calling raw interfaces that require an [out] IXXX ** argument. + + \code + com_ptr foo; + HRESULT hr = pRawInterface->raw_MethodThatReturnsPtr(foo.out()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + interface_pointer* out() throw() + { release(); return &ptr_; } + + //! [in, out] adapter. + /*! + Used when calling raw interfaces that require an [in, out] IXXX ** argument. + + \code + com_ptr foo; + HRESULT hr = pRawInterface->raw_MethodThatChangesPtr(foo.inout()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + interface_pointer* inout() throw() + { return &ptr_; } + + //@} + + private: + class bool_tester + { void operator delete(void*); }; + + public: + operator bool_tester*() const throw() + { if (is_null()) return 0; static bool_tester test; return &test; } + + private: + safe_interface_pointer get_safe_ptr() const throw() + { return reinterpret_cast(ptr_); } + + inline void create_nothrow(const variant_t& v) throw(); + + inline void create(const variant_t& v) throw(com_error); + + void create(const uuid_t& clsid, const com_ptr< ::IUnknown>& outer, DWORD dwClsContext = CLSCTX_ALL) throw(com_error) + { CoCreateInstance(clsid, outer.in(), dwClsContext, iid(), reinterpret_cast(&ptr_)) | raise_exception; } + + void create(const wchar_t* clsidString, const com_ptr< ::IUnknown>& outer, DWORD dwClsContext = CLSCTX_ALL) throw(com_error) + { + if (clsidString == NULL) raise_exception(E_INVALIDARG); + + CLSID clsid; + + if (clsidString[0] == '{') + CLSIDFromString(const_cast(clsidString), &clsid) | raise_exception; + else + CLSIDFromProgID(clsidString, &clsid) | raise_exception; + + create(clsid, outer, dwClsContext);; + } + + void create(const wchar_t* name, BIND_OPTS& bind_opts) throw(com_error) + { CoGetObject(name, &bind_opts, iid(), reinterpret_cast(&ptr_)) | raise_exception; } + }; // class + + //! Comparison with null + /*! + Only comparison with a value of zero is allowed. Non-zero values will result in E_POINTER (wrapped in com_error) being thrown. + + \relates com_ptr + \exception com_error + Throws E_POINTER if a non-zero value is specified. + */ + template inline bool operator==(int null, const com_ptr& x) throw(com_error) + { if(null != 0) raise_exception(E_POINTER); return x.is_null(); } + + //! Comparison with null + /*! + Only comparison with a value of zero is allowed. Non-zero values will result in E_POINTER (wrapped in com_error) being thrown. + + \relates identity_ptr + \exception com_error + Throws E_POINTER if a non-zero value is specified. + */ + static inline bool operator==(int null, const identity_ptr& x) throw(com_error) + { if(null != 0) raise_exception(E_POINTER); return x.is_null(); } + + //! Comparison with null + /*! + Only comparison with a value of zero is allowed. Non-zero values will result in E_POINTER (wrapped in com_error) being thrown. + + \relates com_ptr + \exception com_error + Throws E_POINTER if a non-zero value is specified. + */ + template inline bool operator!=(int null, const com_ptr& x) throw(com_error) + { return x != null; } + + //! Comparison with null + /*! + Only comparison with a value of zero is allowed. Non-zero values will result in E_POINTER (wrapped in com_error) being thrown. + + \relates com_ptr + \exception com_error + Throws E_POINTER if a non-zero value is specified. + */ + inline static bool operator!=(int null, const identity_ptr& x) throw(com_error) + { if(null != 0) raise_exception(E_POINTER); return !x.is_null(); } + + namespace impl { + template + class try_caster_t + { + com_ptr ptr_; + public: + template + try_caster_t( const com_ptr &ptr2) + : ptr_(try_cast(ptr2)) + {} + + const com_ptr &get() const { return ptr_; } + + }; + } + /*! Version of try_cast more to the style of \b dynamic_cast. + This is aimed at being used for casts that get used once in an environment with many interfaces. + \code + com_ptr session; + com_ptr = try_cast( try_cast_ptr(session)->User() ); + \endcode + The impl::try_caster_t is implementation only - let the compiler do the + cast from com_ptr to impl::try_caster_t. Note that try_cast would still + be the preferred method for most casts. + \sa try_cast + \relates com_ptr + */ + template inline com_ptr try_cast_ptr( const impl::try_caster_t &caster) + { return (com_ptr)caster.get(); } + + //! Cast com_ptr. + /*! + Allows QueryInterface when casting to different com_ptr type. + + \code + com_ptr foo; + com_ptr bar; + try { + bar = try_cast(foo); + bar->DoTheThing(); + } catch (com_error&) { + // Cast didn't work. + } + \endcode + + */ + template inline impl::try_cast_t try_cast(const com_ptr& t) { return impl::try_cast_t(t.get()); } + static inline impl::try_cast_t< ::IUnknown> try_cast(const identity_ptr& t) { return impl::try_cast_t< ::IUnknown>(t.get()); } + template inline impl::try_cast_t try_cast(Itf* t) { return impl::try_cast_t(t); } + inline impl::try_cast_t try_cast(const variant_t& v) { return impl::try_cast_t(v); } + //@} + +} // namespace + +#include +#include + +namespace comet { + template + inline void com_ptr::create_nothrow(const variant_t& v) throw() + { + ptr_ = 0; + ::IUnknown *pUnk; + switch (v.get_vt()) { + case VT_DISPATCH: + pUnk = V_DISPATCH(&v.get()); + break; + case VT_UNKNOWN: + pUnk = V_UNKNOWN(&v.get()); + break; + case VT_DISPATCH|VT_BYREF: + pUnk = *V_DISPATCHREF(&v.get()); + break; + case VT_UNKNOWN|VT_BYREF: + pUnk = *V_UNKNOWNREF(&v.get()); + break; + default: + // don't try anything fancy. + return; + } + if( pUnk ==NULL) return; + Unknown_caller_::QueryInterface(pUnk,iid(), reinterpret_cast(&ptr_)); + } + template + inline void com_ptr::create(const variant_t& v) throw(com_error) + { + ptr_ = 0; + ::IUnknown *pUnk; + switch (v.get_vt()) { + case VT_DISPATCH: + pUnk = V_DISPATCH(&v.get()); + break; + case VT_UNKNOWN: + pUnk = V_UNKNOWN(&v.get()); + break; + case VT_DISPATCH|VT_BYREF: + pUnk = *V_DISPATCHREF(&v.get()); + break; + case VT_UNKNOWN|VT_BYREF: + pUnk = *V_UNKNOWNREF(&v.get()); + break; + case VT_EMPTY: + case VT_NULL: + pUnk = NULL; + break; + default: + // don't try anything fancy. + raise_exception( E_INVALIDARG ); + } + if( pUnk ==NULL) return; + Unknown_caller_::QueryInterface(pUnk,iid(), reinterpret_cast(&ptr_)) | raise_exception; + } + + inline identity_ptr::identity_ptr(const impl::try_cast_t& v) throw(com_error) + : ptr_(NULL) + { + VARIANT vv = v.get().get(); + ::IUnknown *pUnk; + switch (V_VT(&vv)) { + case VT_DISPATCH: + pUnk = V_DISPATCH(&vv); + break; + case VT_UNKNOWN: + pUnk = V_UNKNOWN(&vv); + break; + case VT_DISPATCH|VT_BYREF: + pUnk = *V_DISPATCHREF(&vv); + break; + case VT_UNKNOWN|VT_BYREF: + pUnk = *V_UNKNOWNREF(&vv); + break; + case VT_EMPTY: + case VT_NULL: + pUnk = NULL; + break; + default: + // don't try anything fancy. + raise_exception( E_INVALIDARG ); + } + if( pUnk ==NULL) return; + pUnk->QueryInterface( IID_IUnknown, reinterpret_cast(&ptr_)) | raise_exception; + } + + inline identity_ptr::identity_ptr(const impl::com_cast_t& v) throw(com_error) + : ptr_(NULL) + { + VARIANT vv = v.get().get(); + ::IUnknown *pUnk; + switch (V_VT(&vv)) { + case VT_DISPATCH: + pUnk = V_DISPATCH(&vv); + break; + case VT_UNKNOWN: + pUnk = V_UNKNOWN(&vv); + break; + case VT_DISPATCH|VT_BYREF: + pUnk = *V_DISPATCHREF(&vv); + break; + case VT_UNKNOWN|VT_BYREF: + pUnk = *V_UNKNOWNREF(&vv); + break; + default: + // don't try anything fancy. + return; + } + if( pUnk ==NULL) return; + pUnk->QueryInterface( IID_IUnknown, reinterpret_cast(&ptr_)); + } +} + +namespace boost { + template inline Itf* get_pointer(const comet::com_ptr& sp) + { + return sp.raw(); + } + static inline IUnknown* get_pointer(const comet::identity_ptr &sp) + { + return sp.raw(); + } +} + +//! Macro that specialises std::swap for com_ptr. +#define COMET_SPECIALISE_STD_SWAP_ITF(Itf) namespace std { inline void swap(comet::com_ptr& x, comet::com_ptr& y) {x.swap(y); } } + +#endif + diff --git a/lib/local/CamCom/comet/reference_count.h b/lib/local/CamCom/comet/reference_count.h new file mode 100644 index 00000000..020ad36f --- /dev/null +++ b/lib/local/CamCom/comet/reference_count.h @@ -0,0 +1,100 @@ +/** \file + * Implements a class that maintains a reference count. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen, Paul Hollingsworth + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_REFERENCE_COUNT_H +#define COMET_REFERENCE_COUNT_H + +#include +#include + +namespace comet { + /*! \addtogroup Misc + */ + //@{ + /// Simple reference counter. + class reference_count + { + public: + reference_count() : rc_(0) {}; + explicit reference_count(size_t x) : rc_(reinterpret_cast(x)) {}; + + reference_count(const reference_count& x) : rc_(x.rc_) {}; + + enum { FLAGVALUE = 1 }; + bool is_flag() const { + return reinterpret_cast(rc_) == 1; + } + + // is_null + /** Implies that the there are currently no outstanding references + * to this object. + */ + bool is_null() const { + return rc_ == 0; + } + + /// Increment count. + size_t operator++() { + if (!rc_) { + rc_ = new size_t(1); + } + return ++*rc_; + } + + /// Decrement count. + size_t operator--() { + if (rc_) { + if (--*rc_ == 0) { + delete rc_; + rc_ = 0; + return 0; + } + return *rc_; + } + return 0; + } + + void clear() { + rc_ = 0; + } + + void swap(reference_count &rhs) throw() { + std::swap(rc_, rhs.rc_); + } + + reference_count& operator=(reference_count& x) + { + rc_ = x.rc_; + return *this; + } + + private: + size_t* rc_; + }; // class reference_count + //@} +} // namespace comet + +namespace std { + //! Specialisation of std::swap for reference_count. + template<> inline void swap(comet::reference_count &lhs, comet::reference_count &rhs) COMET_STD_SWAP_NOTHROW + { + lhs.swap(rhs); + } +} + +#endif diff --git a/lib/local/CamCom/comet/registry.h b/lib/local/CamCom/comet/registry.h new file mode 100644 index 00000000..3c2d7877 --- /dev/null +++ b/lib/local/CamCom/comet/registry.h @@ -0,0 +1,1302 @@ +/** \file + * Windows Registry iteration and manipulation functions. + */ +/* + * Copyright 2000, 2001 Paul Hollingsworth + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_REGISTRY_H +#define COMET_REGISTRY_H + +#include + +#include +#include +#include + +#ifdef __BORLANDC__ + #define COMET_ITERATOR_VOID(tag) std::iterator +#else + #ifndef __SGI_STL + #ifdef __MINGW32__ + #define COMET_ITERATOR_VOID(tag) std::forward_iterator + #else + #ifdef _CPPLIB_VER + #define COMET_ITERATOR_VOID(tag) std::iterator + #else + #define COMET_ITERATOR_VOID(tag) std::iterator + #endif + #endif + #else + #define COMET_ITERATOR_VOID(tag) std::iterator + #endif +#endif + +namespace comet { + /** Contains implementation of registry classes. + */ + namespace registry { + /// Unicode compatible string class used in registry operations. +// typedef std::basic_string tstring; + + namespace impl { + // Only when an iterator is dereferenced + // do we want to go about reading the value + // from the registry. This requires us to create + // a temporary object. But in order to overload + // operator->, our temporary object must overload + // operator-> - and often it doesn't. + // Proxy is here to effectively overload operator-> for the temporary + // object. + template + class proxy + { + T t_; + public: + proxy(const T &t) : t_(t) {} + T *operator->() + { + return &t_; + } + + const T *operator->() const + { + return &t_; + } + }; + + class key_base + { + HKEY key_; + mutable reference_count rc_; + + static bool valid_key_(HKEY key) { + return key != 0; + } + + void close_() { + if(valid_key_(key_) && (--rc_ == 0)) { + LONG errcode = ::RegCloseKey(key_); + COMET_ASSERT(ERROR_SUCCESS == errcode); + /* C4189 */ errcode; + } + key_ = 0; // invalidate key handle + } + public: + key_base(const key_base &rhs) + : key_(rhs.key_) { + if(valid_key_(key_)) + ++rhs.rc_; + rc_ = rhs.rc_; + } + + key_base(HKEY key = 0) : key_(key) { + } + + ~key_base() throw() { + close_(); + } + + key_base &operator=(const key_base &rhs) + { + key_base tmp(rhs); + swap(tmp); + return *this; + } + + void swap(key_base &rhs) throw() { + std::swap(key_, rhs.key_); + std::swap(rc_, rhs.rc_); + } + + LONG open(const tstring &subkey, + REGSAM sam_desired, + HKEY *childkey) const { + *childkey = 0; + return ::RegOpenKeyEx(key_, + subkey.c_str(), + 0, + sam_desired, + childkey); + } + + HKEY open_nothrow(const tstring &subkey, + REGSAM sam_desired, + LONG *errcode) const { + HKEY childkey_ = 0; + LONG errcode_ = open(subkey, + sam_desired, + &childkey_); + if(errcode) *errcode = errcode_; + return childkey_; + } + + LONG create(const tstring &subkey, + DWORD options, + REGSAM sam_desired, + LPSECURITY_ATTRIBUTES security_attributes, + LPDWORD disposition, + HKEY *childkey) const { + return ::RegCreateKeyEx(key_, + subkey.c_str(), + 0, + REG_NONE, + options, + sam_desired, + security_attributes, + childkey, + disposition); + } + + HKEY create_nothrow(const tstring &subkey, + DWORD options, + REGSAM sam_desired, + LPSECURITY_ATTRIBUTES security_attributes, + LPDWORD disposition, + LONG *errcode) const { + HKEY childkey = 0; + LONG errcode_ = create(subkey, + options, + sam_desired, + security_attributes, + disposition, + &childkey); + if(errcode) *errcode = errcode_; + return childkey; + } + + LONG flush_nothrow() const { + return ::RegFlushKey(key_); + } + + LONG delete_subkey_nothrow(const tstring &subkey) const { + return ::RegDeleteKey(key_, subkey.c_str()); + } + + LONG delete_value_nothrow(const tstring &value_name) const { + return ::RegDeleteValue(key_, value_name.c_str()); + } + + void close() { + close_(); + } + + HKEY get() const { + return key_; + } + + bool is_valid() const throw() + { + return key_ ? true : false; + } + + operator const void *() const throw() + { + return is_valid() ? this : 0; + } + }; // class key_base + + // Enumerating through keys or values is + // very similar. These two class encapsulate + // all that is different between the two algorithms. + struct next_value + { + static LONG perform(HKEY key, + DWORD dwIndex, + LPTSTR lpName, + LPDWORD lpcName) + { + return ::RegEnumValue(key, dwIndex, lpName, lpcName, 0, 0, 0, 0); + } + }; + + struct next_key + { + static LONG perform(HKEY key, + DWORD dwIndex, + LPTSTR lpName, + LPDWORD lpcName) + { + return ::RegEnumKeyEx(key, dwIndex, lpName, lpcName, 0, 0, 0, 0); + } + }; + } // namespace impl + + + /** \struct name_iterator registry.h comet/registry.h + * Iterates through a list of names. The names might be either key + * names or value names, depending on get_next. + */ + template + struct name_iterator : public COMET_ITERATOR_VOID(std::forward_iterator_tag) + { + // key_ is the only assignment that can fail. So + // we make it the first member, so that + // default assignment is exception safe. + impl::key_base key_; + DWORD index_; + DWORD buf_size_; + DWORD num_values_; + + static void check_exception_(LONG errcode) + { + if(ERROR_SUCCESS != errcode) + error_policy::on_error(errcode); + } + + // Is this iterator an end iterator? + bool is_end() const + { + return key_ ? false : true; + } + + // Make this iterator an end + // iterator + void make_end() + { + key_.close(); + } + + public: + typedef tstring value_type; + name_iterator(const impl::key_base &key, + DWORD num_values, + DWORD buf_size) + : key_(key), + num_values_(num_values), + buf_size_(buf_size), + index_(0) + { + if(0 == num_values) + make_end(); + } + + void swap(name_iterator &rhs) + { + key_.swap(rhs.key_); + std::swap(index_, rhs.index_); + std::swap(buf_size_, rhs.buf_size_); + std::swap(num_values_, rhs.num_values_); + } + + name_iterator &operator=(const name_iterator &rhs) + { + key_ = rhs.key_; + index_ = rhs.index_; + buf_size_ = rhs.buf_size_; + num_values_ = rhs.num_values_; + return *this; + } + + impl::key_base key() const + { + return key_; + } + + name_iterator() + { + make_end(); + } + + value_type operator*() const + { + tstring::value_type *buf = static_cast(_alloca(buf_size_)); + DWORD dummy = buf_size_; + check_exception_(get_next::perform(key_.get(), + index_, + buf, + &dummy)); + return buf; + } + + impl::proxy operator->() const + { + return **this; + } + + name_iterator &operator++() + { + ++index_; + if(index_ == num_values_) + make_end(); + return *this; + } + + name_iterator operator++(int) const + { + name_iterator retval = *this; + ++(*this); + return retval; + } + + bool operator==(const name_iterator &rhs) const + { + if(is_end()) return rhs.is_end(); + if(rhs.is_end()) return is_end(); + return index_ == rhs.index_; + } + + bool operator!=(const name_iterator &rhs) const + { + return !(*this == rhs); + } + }; + + /** \class value registry.h comet/registry.h + * A pseudo-reference to a value in the registry. + * Assign to instances of this object to make changes to the corresponding registry value + * Read from this object to read values from the registry. + */ + template + class value + { + impl::key_base key_; + tstring value_name_; + static void check_exception_(LONG errcode) + { + if(ERROR_SUCCESS != errcode) + error_policy::on_error(errcode); + } + static void type_mismatch() + { + error_policy::on_typemismatch(); + } + + LONG get_value_(DWORD *type, + BYTE *buffer, + DWORD *number_bytes) const + { + return ::RegQueryValueEx(key_.get(), + value_name_.c_str(), + 0, + type, + buffer, + number_bytes); + } + + void get_value(DWORD *type, + BYTE *buffer, + DWORD *number_bytes) const { + check_exception_( + get_value_(type, + buffer, + number_bytes) + ); + } + + LONG set_value_(DWORD type, + const BYTE *data, + DWORD number_bytes) + { + return ::RegSetValueEx(key_.get(), + value_name_.c_str(), + 0, + type, + data, + number_bytes); + } + + void set_value(DWORD type, + const BYTE *data, + DWORD number_bytes) + { + check_exception_( + set_value_(type, + data, + number_bytes)); + } + + tstring name() const + { + return value_name_; + } + public: + + //! This can be used to query if a value exists. + /*! + For example: + \code + string get_thread_model(const regkey &clsid_key) + { + regkey::value_type t = clsid_key.open("InprocServer32", KEY_READ)["ThreadingModel"]; + if(t.exists()) + return t.str(); + else + return "Single"; + } + \endcode + */ + bool exists() const + { + return ERROR_SUCCESS == get_value_(0, 0, 0); + } + + value(const impl::key_base &key, + const tstring &value_name) + : key_(key), + value_name_(value_name) + { + } + + //! Non throwing swap + /*! This is for efficiency only. + operator= is overloaded to have a different + meaning (copying one part of the + registry to another part of the registry). + */ + void swap(value &rhs) + { + key_.swap(rhs.key_); + value_name_.swap(rhs.value_name_); + } + + + //! Get a value of any type. The arguments are passed directly to RegQueryValueEx + /*! + \param type Pointer to the type - can be 0 + \param buffer Pointer to a buffer - can be 0 + \param number_bytes Indicates size of the buffer - can be 0 if buffer is 0 + */ + void get(DWORD *type, + BYTE *buffer, + DWORD *number_bytes) const + { + get_value(type, + buffer, + number_bytes); + } + + //! Get a value - return errcode + /*! + \param type Pointer to the type - can be 0 + \param buffer Pointer to a buffer - can be 0 + \param number_bytes Indicates size of the buffer - can be 0 if buffer is 0 + */ + LONG get_nothrow(DWORD *type, + BYTE *buffer, + DWORD *number_bytes) const + { + return get_value_(type, buffer, number_bytes); + } + + //! Set a value arbitrarily. The arguments are passed directly to RegSetValueEx + /*! + \param type Type to set it to (e.g. REG_SZ) + \param buffer Pointer to a buffer + \param number_bytes Indicates size of the buffer + */ + void set(DWORD type, + const BYTE *buffer, + DWORD number_bytes) + { + set_value(type, + buffer, + number_bytes); + } + + //! Set a value - return errcode + /*! + \param type Type to set it to (e.g. REG_SZ) + \param buffer Pointer to a buffer + \param number_bytes Indicates size of the buffer + */ + LONG set_nothrow(DWORD type, + const BYTE *buffer, + DWORD number_bytes) + { + return set_value_(type, buffer, number_bytes); + } + + //! Interpret value as a string + /*! + \exception com_error If the type is not REG_SZ or REG_EXPAND_SZ (using standard error_policy) + */ + tstring str() const + { + DWORD number_bytes = 0; + DWORD type; + get_value(&type, + 0, + &number_bytes); + if( (REG_SZ != type) && (REG_EXPAND_SZ != type)) + type_mismatch(); + BYTE *buffer = static_cast(_alloca(number_bytes)); + get_value(0, + buffer, + &number_bytes); + return tstring(reinterpret_cast(buffer), + (number_bytes/sizeof(tstring::value_type)) - 1); + } + + tstring str(const tstring& default_val) const + { + DWORD number_bytes = 0; + DWORD type; + if (get_value_(&type, + 0, + &number_bytes) != ERROR_SUCCESS) return default_val; + if( (REG_SZ != type) && (REG_EXPAND_SZ != type)) + type_mismatch(); + BYTE *buffer = static_cast(_alloca(number_bytes)); + get_value(0, + buffer, + &number_bytes); + return tstring(reinterpret_cast(buffer), + (number_bytes/sizeof(tstring::value_type)) - 1); + } + + //! Implicit conversion to string. + operator tstring() const + { + return str(); + } + + //! Implicit conversion to unsigned int + operator DWORD() const + { + return dword(); + } + + //! Interpret value as a DWORD + /*! + \exception com_error If the type is not REG_DWORD or REG_DWORD_LITTLE_ENDIAN (using standard error_policy) + */ + DWORD dword() const + { + DWORD number_bytes = sizeof( DWORD ); + DWORD type; + DWORD retval; + get_value(&type, + reinterpret_cast(&retval), + &number_bytes); + if( (REG_DWORD != type) && (REG_DWORD_LITTLE_ENDIAN != type) ) + type_mismatch(); + return retval; + } + + DWORD dword(DWORD default_val) const + { + DWORD number_bytes = sizeof( DWORD ); + DWORD type; + DWORD retval; + if (get_value_(&type, + reinterpret_cast(&retval), + &number_bytes) != ERROR_SUCCESS) return default_val; + if( (REG_DWORD != type) && (REG_DWORD_LITTLE_ENDIAN != type) ) + type_mismatch(); + return retval; + } + + // These two operators are currently useless + // because VC++ 6.0 appears to have a bug - + // it claims that no conversion to pair + // exists even though it clearly does here. + // However, it's possible these would be + // useful on other compilers (or future compilers). + operator std::pair() const + { + return std::make_pair(name(), str()); + } + + operator std::pair() const + { + return std::make_pair(name(), dword()); + } + + //! Assign a string value and set the type to REG_SZ + value &operator=(const tstring &rhs) + { + set_value(REG_SZ, + reinterpret_cast(rhs.c_str()), + (rhs.length() + 1) * sizeof ( tstring::value_type)); + return *this; + } + + //! Assign a DWORD value and set the type to REG_DWORD + value &operator=(const DWORD &rhs) + { + set_value(REG_DWORD, + reinterpret_cast(&rhs), + sizeof (DWORD) ); + return *this; + } + + //! Assign an integer value - sets type to REG_DWORD + value &operator=(int rhs) + { + return *this = DWORD(rhs); + } + + //! Assign value from another registry value + /*! + Because value objects always refer to a part of the registry, + this effectively copies a registry value + from somewhere else in the registry. + */ + value &operator=(const value &rhs) + { + DWORD type; + DWORD size; + rhs.get_value(&type, + 0, + &size); + BYTE *buffer = static_cast(_alloca(size)); + rhs.get_value_(0, + buffer, + &size); + set_value(type, + buffer, + size); + return *this; + } + }; // class value + + /** \class const_value_iterator registry.h comet/registry.h + * Forward const iterator over registry values. + */ + template + class const_value_iterator : public COMET_ITERATOR_VOID(std::forward_iterator_tag) + { + typedef const value second_type; + typedef std::pair value_type_; + + value_type_ get_value() const + { + tstring name = *index_; + return std::make_pair(name, second_type(index_.key(), name) ); + } + protected: + name_iterator index_; + public: + typedef value_type_ value_type; + + const_value_iterator(const impl::key_base &key, + DWORD num_values, + DWORD buf_size) + : index_(key, num_values, buf_size) + { + } + + const_value_iterator() {} + + void swap(const_value_iterator &rhs) + { + index_.swap(rhs.index_); + } + + value_type operator*() const + { + return get_value(); + } + + const_value_iterator &operator++() + { + ++index_; + return *this; + } + + const_value_iterator operator++(int) + { + const_value_iterator retval(*this); + ++(*this); + return retval; + } + + impl::proxy operator->() + { + return **this; + } + + bool operator==(const const_value_iterator &rhs) const + { + return index_ == rhs.index_; + } + + bool operator!=(const const_value_iterator &rhs) const + { + return index_ != rhs.index_; + } + }; + + /** \class value_iterator registry.h comet/registry.h + * Forward iterator over registry values. + */ + template + class value_iterator : public const_value_iterator + { + typedef value second_type; + typedef std::pair value_type_; + typedef const_value_iterator base; + value_type_ get_value() const + { + tstring name = *this->index_; + return std::make_pair(name, second_type(this->index_.key(), name) ); + } + + public: + typedef value_type_ value_type; + value_iterator(const impl::key_base &key, + DWORD num_values, + DWORD buf_size) + : base(key, num_values, buf_size) {} + + value_iterator() {} + + value_type operator*() const + { + return get_value(); + } + + value_iterator &operator++() + { + base::operator++(); + return *this; + } + + value_iterator operator++(int) + { + value_iterator retval(*this); + ++(*this); + return retval; + } + + impl::proxy operator->() + { + return get_value(); + } + + bool operator==(const value_iterator &rhs) const + { + return this->index_ == rhs.index_; + } + + bool operator!=(const value_iterator &rhs) const + { + return this->index_ != rhs.index_; + } + }; + + /** \struct collection registry.h comet/registry.h + * STL style container class for various types of registry based + * aggregations. + */ + template + struct collection + { + impl::key_base key_; + DWORD num_values_; + DWORD buf_size_; + + public: + collection(const impl::key_base &key, + DWORD num_values, + DWORD buf_size) + : key_(key), + num_values_(num_values), + buf_size_(buf_size) + { + } + + typedef iterator_ iterator; + typedef const_iterator_ const_iterator; + typedef typename iterator_::value_type value_type; + typedef size_t size_type; + + //! Number of elements in the collection + size_type size() const { return num_values_; } + + //! Exception safe swap + void swap(collection &rhs) + { + key_.swap(rhs.key_); + std::swap(num_values_, rhs.num_values_); + std::swap(buf_size_, rhs.buf_size_); + } + + //! Get the first iterator + iterator begin() + { + return iterator(key_, num_values_, buf_size_); + } + + //! Signature iterator marking the end of the sequence + iterator end() + { + return iterator(); + } + + const_iterator begin() const + { + return const_iterator(key_, num_values_, buf_size_); + } + + const_iterator end() const + { + return const_iterator(); + } + }; + + /** \class info registry.h comet/registry.h + * Structure returned by regkey.enumerate() + */ + template + class info + { + // Make key_ the first member - this + // ensures exception safe assignment. + impl::key_base key_; + + // Number of values + DWORD num_values_; + + // Maximum length of a value name + DWORD value_name_tchars_; + + // Number of sub keys + DWORD num_subkeys_; + + // Maximum length of a sub key name + DWORD subkey_name_tchars_; + + static void check_exception_(LONG errcode) + { + if(ERROR_SUCCESS != errcode) + error_policy::on_error(errcode); + } + + static DWORD bytes(DWORD tchars) + { + return (tchars + 1) * sizeof( tstring::value_type); + } + public: + info(const impl::key_base &key) + : key_(key) + { + check_exception_(::RegQueryInfoKey(key_.get(), + 0, // lpClass - reserved + 0, // lpcClass - reserved + 0, // lpReserved + &num_subkeys_, + &subkey_name_tchars_, + 0, // lpcMaxClassLen - I think this is also reserved + &num_values_, + &value_name_tchars_, + 0, // lpcMaxValueLen - not necessary for us + 0, // lpcbSecurityDescriptor + 0)); // lpftLastWriteTime + } + + //! Exception safe swap + void swap(info &rhs) + { + key_.swap(rhs.key_); + std::swap(num_subkeys_, rhs.num_subkeys_); + std::swap(subkey_name_tchars_, rhs.subkey_name_tchars_); + std::swap(value_name_tchars_, rhs.value_name_tchars_); + std::swap(num_values_, rhs.num_values_); + } + + //! Type returned by values() + typedef collection, const_value_iterator > values_type; + + //! Type returned by value_names() + typedef collection > value_names_type; + + //! Type returned by subkeys() + typedef collection > subkeys_type; + + //! Number of values under this key (excluding the default value) + size_t num_values() const + { + return num_values_; + } + + //! Number of subkeys under this key + size_t num_subkeys() const + { + return num_subkeys_; + } + + //! Length of the longest value_name under this key (in TCHARs) + size_t max_value_name() const + { + return value_name_tchars_; + } + + //! Length of the longest subkey name under this key (in TCHARs) + size_t max_subkey_name() const + { + return subkey_name_tchars_; + } + + //! Return the collection of values + /*! + The value_type of the collection is std::pair, regkey::mapped_type> + regkey::mapped_type has implicit conversions to unsigned int and std::basic_string however. + Example - copy all value-name/value pairs into a map + \code + typedef std::basic_string tstring; + regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate()); + map values_map; + copy(info.values().begin(), info.values().end(), inserter(values_map, values_map.end())); + \endcode + Example - copy all value-name/value pairs into a string map - exception will + be thrown if a non string value is encountered. + \code + typedef std::basic_string tstring; + regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate()); + map values_map; + copy(info.values().begin(), info.values().end(), inserter(values_map, values_map.end())); + \endcode + Example - copy all value-name/value pairs into a string/int map - exception will + be thrown if a non string value is encountered. + \code + typedef std::basic_string tstring; + regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate()); + map values_map; + copy(info.values().begin(), info.values().end(), inserter(values_map, values_map.end())); + \endcode + */ + values_type values() const + { + return values_type(key_, num_values_, bytes(value_name_tchars_)); + } + + //! Returns the collection of value names + /*! The value_type of the collection is std::basic_string. + Example - copy all value names of HKEY_LOCAL_MACHINE into a list + \code + regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate()); + vector value_names; + copy(info.value_names().begin(), info.value_names().end(), back_inserter(value_names)); + \endcode + */ + value_names_type value_names() const + { + return value_names_type(key_, num_values_, bytes(value_name_tchars_)); + } + + //! Returns the collection of subkey names + /*! The value_type of the collection is std::basic_string. + Example - copy all subkeys of HKEY_LOCAL_MACHINE into a list + \code + regkey::info_type info(regkey(HKEY_LOCAL_MACHINE).enumerate()); + vector subkey_names; + copy(info.subkeys().begin(), info.subkeys().end(), back_inserter(subkey_names)); + \endcode + */ + subkeys_type subkeys() const + { + return subkeys_type(key_, num_subkeys_, bytes(subkey_name_tchars_)); + } + }; + +#ifdef __BORLANDC__ + using impl::key_base; +#endif + + /** \class key registry.h comet/registry.h + * Registry key wrapper. + Because an HKEY cannot be duplicated in a platform independent way, + reference counting is used to enable copying of key objects. + + Methods with the _nothrow suffix do not throw exceptions other than + std::bad_alloc. + + Key instances can be used as output iterators for std::pair + assignments. The second argument of each pair must be assignable to + an instance of regkey::mapped_type. Currently, this means that the + second member of each pair must be convertable to either an int, + unsigned int, or std::basic_string. + Example: + \code + regkey the_key = regkey(HKEY_LOCAL_MACHINE).open("Software\\Comet"); + map names; + names["one"] = "one"; + names["two"] = "two"; + copy(names.begin(), names.end(), the_key); + map values; + values["three"] = 3; + values["four"] = 4; + copy(values.begin(), values.end(), the_key); + \endcode + */ + template + class key : private impl::key_base { + private: + static void check_exception_(LONG errcode) + { + if(ERROR_SUCCESS != errcode) + error_policy::on_error(errcode); + } + public: + key(HKEY key_handle = 0) : key_base(key_handle) {} + + //! Copy a key. + /*! In reality this + only increments a reference count. + We have to use reference counting + because ::DuplicateHandle does not + work for registry keys on windows 95/98. + */ + + key(const key &rhs) + : key_base(rhs) { + } + //! Copy a key. + /*! This is useful for attaching to keys from a name iterator. + */ + key( const impl::key_base &rhs) + : key_base(rhs) { + } + + void swap(key &rhs) + { + key_base::swap(rhs); + } + + //! Operator overload to allow you to put key instances in a conditional. + /*! This operator allows you to write code like this: + \code + if(subkey = key(HKEY_LOCAL_MACHINE).open_nothrow(_T("Software"))) + { + ... + }; + \endcode + const void * is used instead of the more obvious "bool" to disable + implicit conversions to int and the like. + Example: + \code + int success = key(HKEY_LOCAL_MACHINE).open_nothrow(_T("Software")); // Won't compile fortunately!! + \endcode + */ + operator const void *() const throw() + { + return is_valid() ? this : 0; + } + + + //! Open a subkey + key open(const tstring &subkey, + REGSAM sam_desired = KEY_ALL_ACCESS) const { + HKEY childkey_ = 0; + check_exception_( + key_base::open(subkey, + sam_desired, + &childkey_) + ); + return childkey_; + } + + //! Open a subkey, no exceptions + key open_nothrow(const tstring &subkey, + REGSAM sam_desired = KEY_ALL_ACCESS, + LONG *errcode = 0) const { + return key_base::open_nothrow(subkey, + sam_desired, errcode); + } + + //! Create a subkey + key create(const tstring &subkey, + DWORD options = REG_OPTION_NON_VOLATILE, + REGSAM sam_desired = KEY_ALL_ACCESS, + LPSECURITY_ATTRIBUTES security_attributes = 0, + LPDWORD disposition = 0) const { + HKEY childkey_ = 0; + check_exception_( + key_base::create(subkey, + options, + sam_desired, + security_attributes, + disposition, + &childkey_) + ); + return childkey_; + } + + //! Create a subkey - no exceptions + key create_nothrow(const tstring &subkey, + DWORD options = REG_OPTION_NON_VOLATILE, + REGSAM sam_desired = KEY_ALL_ACCESS, + LPSECURITY_ATTRIBUTES security_attributes = 0, + LPDWORD disposition = 0, + LONG *errcode = 0) const { + return key_base::create_nothrow(subkey, + options, + sam_desired, + security_attributes, + disposition, + errcode); + } + + //! Call RegFlushKey with the contained key + void flush() const { + check_exception_( + flush_nothrow() + ); + } + + //! Call RegFlushKey with the contained key - no exceptions + LONG flush_nothrow() const { + return key_base::flush_nothrow(); + } + + //! Delete the subkey + /*! Warning - the behaviour is different + between WinNT and Win95/98 if + sub keys are present. + See documentation for ::RegDeleteKey for + more information. + */ + void delete_subkey(const tstring &subkey) const { + check_exception_( + delete_subkey_nothrow(subkey.c_str()) + ); + } + + //! Delete the subkey + /*! Warning - the behaviour is different + between WinNT and Win95/98 if + sub keys are present. + See documentation for ::RegDeleteKey for + more information. + */ + LONG delete_subkey_nothrow(const tstring &subkey) const { + return key_base::delete_subkey_nothrow(subkey); + } + + //! delete a value + void delete_value(const tstring &value_name) const { + check_exception_( + delete_value_nothrow(value_name) + ); + } + + //! delete a value - no exceptions + LONG delete_value_nothrow(const tstring &value_name) const { + return key_base::delete_value_nothrow(value_name); + } + + //! Release reference to the key. + /*! Note that this will only call ::RegCloseKey + if this was the last reference to the outstanding key. + This method is implicitly called by the destructor. + */ + void close() { + key_base::close(); + } + + //! Get access to the raw key without releasing ownership + HKEY get() const { + return key_base::get(); + } + + typedef value mapped_type; + + // All of these methods are const because I figured + // it was more useful. It allows you to create + // temporary regkey objects for the purposes of + // updating. + + //! Get a reference to a value in the registry. + /*! The returned value can be used on both sides of an assignment. Example: + \code + key.get_value("Name") = "Paul"; + string name = key.get_value("Name"); + cout << key.get_value("Name").str() << endl; + \endcode + */ + mapped_type get_value(const tstring &value_name = _T("")) const + { + return mapped_type(*this, value_name); + } + + //! Subscript operator overload - syntactic sugar for get_value(). + /*! Example: + \code + key["Name"] = "Paul"; + string name = key["Name"]; + cout << key["Name"].str() << endl; + \endcode + */ + mapped_type operator[](const tstring &value_name) const + { + return get_value(value_name); + } + + //! Type returned by enumerate() + typedef info info_type; + //! Type returned by enumerate().subkeys() + typedef typename info_type::subkeys_type subkeys_type; + //! Type returned by enumerate().values() + typedef typename info_type::values_type values_type; + //! Type returned by enumerate().value_names() + typedef typename info_type::value_names_type value_names_type; + + //! Enumerate the subkeys, values or value_names, or obtain other information about the key. See also info. + /*! + The enumerate() method effectively calls RegQueryInfoKey, so you should + minimize the number of calls to enumerate() if efficiency is + a concern. e.g. + The following + \code + regkey::info_type info = key.enumerate(); + copy(info.values().begin(), info.values().end(), inserter(value_map, value_map.end())); + \endcode + is more efficient than + \code + copy(key.enumerate().values().begin(), key.enumerate().values().end(), inserter(value_map, value_map.end())); + \endcode + The second version will end up calling RegQueryInfoKey twice. + */ + info_type enumerate() const + { + return info_type(*this); + } + + //! Part of making key into an output iterator + key &operator*() + { + return *this; + } + + //! Assignment. This is designed to work with 'std::pair's - the value type of map classes + template + key &operator=(const T &val) + { + get_value(val.first) = val.second; + return *this; + } + + //! Exception safe assignment operator. + //! Can still throw std::bad_alloc due + //! to reference counting. + //template<> + key &operator=(const key &rhs) + { + key_base::operator=(rhs); + return *this; + } + + //! Noop increment + key &operator++() { return *this; } + //! Noop decrement + key &operator++(int) { return *this; } + }; // class key + } // namespace registry +} // namespace comet + +#endif // COMET_REGISTRY_H diff --git a/lib/local/CamCom/comet/regkey.h b/lib/local/CamCom/comet/regkey.h new file mode 100644 index 00000000..882dcd29 --- /dev/null +++ b/lib/local/CamCom/comet/regkey.h @@ -0,0 +1,65 @@ +/** \file + * Base regkey type and error-policy definition. + */ +/* + * Copyright 2000, 2001 Paul Hollingsworth + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_REGKEY_H +#define COMET_REGKEY_H + +#include + +#include +#include + +namespace comet +{ + /** \struct reg_error regkey.h comet/regkey.h + * Standard error policy, mainly for use by registry functions. + */ + struct reg_error + { + static void on_error(LONG errcode) + { + throw com_error(HRESULT_FROM_WIN32(errcode)); + } + + static void on_typemismatch() + { + throw com_error(HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE)); + } + }; + + /** Standard type for use when dealing with registry keys. + */ + typedef registry::key regkey; + +} + +namespace std { + + COMET_DECLARE_SWAP(comet::regkey) + COMET_DECLARE_SWAP(comet::regkey::info_type) + COMET_DECLARE_SWAP(comet::regkey::mapped_type) + COMET_DECLARE_SWAP(comet::regkey::values_type) + COMET_DECLARE_SWAP(comet::regkey::subkeys_type) + COMET_DECLARE_SWAP(comet::regkey::value_names_type) + COMET_DECLARE_SWAP(comet::regkey::values_type::iterator) + COMET_DECLARE_SWAP(comet::regkey::values_type::const_iterator) + COMET_DECLARE_SWAP(comet::regkey::subkeys_type::iterator) + COMET_DECLARE_SWAP(comet::regkey::value_names_type::iterator) +} + +#endif diff --git a/lib/local/CamCom/comet/safearray.h b/lib/local/CamCom/comet/safearray.h new file mode 100644 index 00000000..6851e9b6 --- /dev/null +++ b/lib/local/CamCom/comet/safearray.h @@ -0,0 +1,1498 @@ +/** \file + * SafeArray wrapper implementation. + */ +/* + * Copyright 2000, 2001, 2002 Sofus Mortensen, Michael Geddes + * Copyright 2012 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_SAFEARRAY_H +#define COMET_SAFEARRAY_H + +#include +#include +#include +#include +#include +//#include +#include +#include +#include + +#include +#include +#include + +#ifndef NDEBUG +#define COMET_ITERATOR_DEBUG +#endif + +namespace comet { + + + namespace impl { + + template + struct access_operator + { + // Has operator -> + template + struct base + { + T *operator->() + { + return &(static_cast(this)->operator*()); + } + }; + }; + // Doesn't have operator-> + template<> + struct access_operator + { + template + struct base + { + }; + }; + + template struct sa_traits; //Moved to common.h + + template class sa_iterator; + + template struct const_traits { + typedef T value_type; + typedef typename sa_traits::const_reference reference; + typedef const T* pointer; + }; + + template struct nonconst_traits { + typedef T value_type; + typedef typename sa_traits::reference reference; + typedef T* pointer; + }; + + + template<> struct sa_traits : public basic_sa_traits {}; + template<> struct sa_traits : public basic_sa_traits {}; + + template<> struct sa_traits : public basic_sa_traits {}; + template<> struct sa_traits : public basic_sa_traits {}; + + template<> struct sa_traits : public basic_sa_traits {}; + template<> struct sa_traits : public basic_sa_traits {}; + template<> struct sa_traits : public basic_sa_traits {}; + + template<> struct sa_traits : public basic_sa_traits {}; + template<> struct sa_traits : public basic_sa_traits {}; + + template<> struct sa_traits + { + typedef VARIANT raw; + + enum { vt = VT_VARIANT }; + enum { check_type = impl::stct_features_ok }; + enum { extras_type = stet_null }; + + typedef variant_t value_type; + typedef variant_t& reference; + typedef const variant_t& const_reference; + + static reference create_reference(raw& x) { return *reinterpret_cast(&x); } + static const_reference create_const_reference(raw& x) { return *reinterpret_cast(&x); } + + typedef sa_iterator > iterator; + typedef sa_iterator > const_iterator; + + static bool are_features_ok(unsigned short f) { return (f & FADF_VARIANT) != 0; } + static com_ptr get_record_info() { return 0; } + }; + + template<> struct sa_traits + { + enum { vt = VT_BSTR }; + enum { check_type = impl::stct_vt_ok }; + enum { extras_type = stet_null }; + + typedef BSTR raw; + typedef bstr_t value_type; + typedef bstr_t& reference; + typedef const bstr_t& const_reference; + + static reference create_reference(raw& x) { return *reinterpret_cast(&x); } + static const_reference create_const_reference(raw& x) { return *reinterpret_cast(&x); } + + typedef sa_iterator > iterator; + typedef sa_iterator > const_iterator; + + static bool are_features_ok(unsigned short f) { return (f & FADF_BSTR) != 0; } + static com_ptr get_record_info() { return 0; } + }; + + template<> struct sa_traits + { + enum { vt = VT_CY }; + enum { check_type = impl::stct_vt_ok }; + enum { extras_type = impl::stet_null }; + + typedef CY raw; + typedef currency_t value_type; + typedef currency_t& reference; + typedef const currency_t& const_reference; + + static reference create_reference(raw& x) { return *reinterpret_cast(&x); } + static const_reference create_const_reference(raw& x) { return *reinterpret_cast(&x); } + + typedef sa_iterator > iterator; + typedef sa_iterator > const_iterator; + + static bool are_features_ok(unsigned short) { return true; } + static com_ptr get_record_info() { return 0; } + }; + + template<> struct sa_traits + { + enum { vt = VT_DATE }; + enum { check_type = impl::stct_vt_ok }; + enum { extras_type = impl::stet_null }; + + typedef DATE raw; + typedef datetime_t value_type; + typedef datetime_t& reference; + typedef const datetime_t& const_reference; + + static reference create_reference(raw& x) { return *reinterpret_cast(&x); } + static const_reference create_const_reference(raw& x) { return *reinterpret_cast(&x); } + + typedef sa_iterator > iterator; + typedef sa_iterator > const_iterator; + + static bool are_features_ok(unsigned short) { return true; } + static com_ptr get_record_info() { return 0; } + }; + + template<> struct sa_traits + { + enum { vt = VT_BOOL }; + enum { check_type = impl::stct_vt_ok }; + enum { extras_type = impl::stet_null }; + + typedef VARIANT_BOOL raw; + typedef variant_bool_t value_type; + typedef variant_bool_t& reference; + typedef const variant_bool_t& const_reference; + + static reference create_reference(raw& x) { return *reinterpret_cast(&x); } + static const_reference create_const_reference(raw& x) { return *reinterpret_cast(&x); } + + typedef sa_iterator > iterator; + typedef sa_iterator > const_iterator; + + static bool are_features_ok(unsigned short) { return true; } + static com_ptr get_record_info() { return 0; } + }; + + template<> struct sa_traits: sa_traits + { + }; + + template<> struct sa_traits< com_ptr< ::IUnknown> > + { + enum { vt = VT_UNKNOWN }; + enum { check_type = impl::stct_vt_ok }; + enum { extras_type = impl::stet_iid }; + + typedef IUnknown* raw; + typedef com_ptr< ::IUnknown> value_type; + typedef com_ptr< ::IUnknown>& reference; + typedef const com_ptr< ::IUnknown>& const_reference; + + static reference create_reference(raw& x) { return *reinterpret_cast*>(&x); } + static const_reference create_const_reference(raw& x) { return *reinterpret_cast*>(&x); } + + typedef sa_iterator, nonconst_traits > > iterator; + typedef sa_iterator, const_traits > > const_iterator; + + static bool are_features_ok(unsigned short f) { return (f & (FADF_UNKNOWN|FADF_DISPATCH)) != 0; } + static com_ptr get_record_info() { return 0; } + static const uuid_t& iid() { return uuid_t::create_const_reference(IID_IUnknown); } + }; + + template<> struct sa_traits< com_ptr< ::IDispatch> > + { + enum { vt = VT_DISPATCH }; + enum { check_type = impl::stct_vt_ok }; + enum { extras_type = impl::stet_iid }; + + typedef IDispatch* raw; + typedef com_ptr< ::IDispatch> value_type; + typedef com_ptr< ::IDispatch>& reference; + typedef const com_ptr< ::IDispatch>& const_reference; + + static reference create_reference(raw& x) { return *reinterpret_cast*>(&x); } + static const_reference create_const_reference(raw& x) { return *reinterpret_cast*>(&x); } + + typedef sa_iterator, nonconst_traits > > iterator; + typedef sa_iterator, const_traits > > const_iterator; + + static bool are_features_ok(unsigned short f) { return (f & FADF_DISPATCH) != 0; } + static com_ptr get_record_info() { return 0; } + + static const uuid_t& iid() { return uuid_t::create_const_reference(IID_IDispatch); } + }; + +#ifdef COMET_ITERATOR_DEBUG +#define COMET_SAIT_THIS ,this +#define COMET_SAIT_ITER(CONT_, IT_, TRAITS_) impl::sa_debug_iterator + + template + struct sa_debug_traits + { + typedef TRAITS traits; + typedef typename TRAITS::value_type value_type; + typedef typename TRAITS::raw raw; + typedef typename TRAITS::reference reference; + typedef typename TRAITS::iterator nonconst_iterator; + typedef typename TRAITS::iterator iterator; + typedef typename TRAITS::const_iterator const_iterator; + }; + template + struct sa_const_debug_traits + { + typedef TRAITS traits; + typedef typename TRAITS::value_type value_type; + typedef typename TRAITS::raw raw; + typedef typename TRAITS::const_reference reference; + typedef typename TRAITS::iterator nonconst_iterator; + typedef typename TRAITS::const_iterator iterator; + typedef typename TRAITS::const_iterator const_iterator; + }; + + template< typename CONT, typename TRAITS> + class sa_debug_iterator : public std::iterator, + public access_operator::result>::template base > + { + public: + const CONT *cont_; + typename TRAITS::iterator iter_; + + template + sa_debug_iterator(IT ptr, const CONT *cont) : iter_(ptr), cont_(cont) {} + + sa_debug_iterator( const sa_debug_iterator > &nc_it ) : iter_(nc_it.iter_), cont_(nc_it.cont_) {} + + sa_debug_iterator(): cont_(NULL) {} + + typename TRAITS::iterator get_raw()const { return iter_; } + typename TRAITS::const_iterator get_const_raw()const { return iter_; } + + + sa_debug_iterator operator++(int) { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( get_const_raw() < cont_->end().get_raw() ); + sa_debug_iterator t(*this); + ++iter_; + return t; + } + + sa_debug_iterator& operator++() { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( get_const_raw() < cont_->end().get_raw() ); + ++iter_; + return *this; + } + + sa_debug_iterator operator--(int) { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( get_const_raw() > cont_->begin().get_raw() ); + sa_debug_iterator t(*this); + --iter_; + return t; + } + + sa_debug_iterator& operator--() { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( get_const_raw() > cont_->begin().get_raw() ); + --iter_; + return *this; + } + + typename TRAITS::reference operator[](size_t n) { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( (get_const_raw()+ n) >= cont_->begin().get_raw()); + COMET_ASSERT( (get_const_raw()+n) < cont_->end().get_raw() ); + return iter_[n]; + } + + sa_debug_iterator& operator+=(size_t n) { + COMET_ASSERT(cont_!=NULL); + COMET_ASSERT((get_const_raw()+ n) >= cont_->begin().get_raw()); + COMET_ASSERT((get_const_raw()+n) <= cont_->end().get_raw() ); + iter_ += n; + return *this; + } + + sa_debug_iterator& operator-=(size_t n) { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( (get_const_raw()- n) >= cont_->begin().get_raw()); + COMET_ASSERT( (get_const_raw()- n) <= cont_->end().get_raw() ); + iter_ -= n; + return *this; + } + + ptrdiff_t operator-(const sa_debug_iterator& it) const { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( cont_ == it.cont_); + return iter_ - it.iter_; + } + + bool operator<(const sa_debug_iterator& it) const { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( cont_ == it.cont_); + return iter_ < it.iter_; + } + + bool operator>(const sa_debug_iterator& it) const { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( cont_ == it.cont_); + return iter_ > it.iter_; + } + + bool operator<=(const sa_debug_iterator& it) const { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( cont_ == it.cont_); + return iter_ <= it.iter_; + } + + bool operator>=(const sa_debug_iterator& it) const { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( cont_ == it.cont_); + return iter_ >= it.iter_; + } + + bool operator==(const sa_debug_iterator& it) const { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( cont_ == it.cont_); + return iter_ == it.iter_; + } + + bool operator!=(const sa_debug_iterator& it) const { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( cont_ == it.cont_); + return iter_ != it.iter_; + } + + sa_debug_iterator operator+(size_t n) const { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( (get_const_raw() + n) >= cont_->begin().get_raw()); + COMET_ASSERT( (get_const_raw() + n) <= cont_->end().get_raw() ); + return sa_debug_iterator( iter_+n, cont_); + } + + sa_debug_iterator operator-(size_t n) const { + COMET_ASSERT( cont_!=NULL); + COMET_ASSERT( (get_const_raw() - n) >= cont_->begin().get_raw()); + COMET_ASSERT( (get_const_raw() - n) <= cont_->end().get_raw() ); + return sa_debug_iterator( iter_-n, cont_); + } + + typename TRAITS::reference operator*() { + COMET_ASSERT( cont_ != NULL); + COMET_ASSERT( (get_const_raw()) >= cont_->begin().get_raw()); + COMET_ASSERT( (get_const_raw()) < cont_->end().get_raw() ); + return *iter_; + } + }; + + +#else // COMET_ITERATOR_DEBUG +#define COMET_IT_DBG__(x) +#define COMET_SAIT_THIS +#define COMET_SAIT_ITER(CONT_, IT_, TRAITS_) IT_ +#endif // COMET_ITERATOR_DEBUG + + + /** \internal + */ + template class sa_iterator : public std::iterator, + public access_operator::result>::template base< T, sa_iterator > + + { + typedef sa_iterator > nonconst_self; + public: + typedef sa_traits traits; + typename traits::raw* ptr_; + + typedef typename TR::pointer pointer; + typedef typename TR::reference reference; + typedef ptrdiff_t difference_type; + + sa_iterator(const nonconst_self& it ) + : ptr_(it.get_raw()) + {} + + explicit sa_iterator(typename traits::raw* ptr) : ptr_(ptr) {} + + sa_iterator() {} + + typename traits::raw* get_raw() const + { + return ptr_; + } + + sa_iterator operator++(int) { + sa_iterator t(*this); + ++ptr_; + return t; + } + + sa_iterator& operator++() { + ++ptr_; + return *this; + } + + sa_iterator operator--(int) { + sa_iterator t(*this); + --ptr_; + return t; + } + + sa_iterator& operator--() { + --ptr_; + return *this; + } + + reference operator[](size_t n) { + return traits::create_reference(ptr_[n]); + } + + sa_iterator& operator+=(size_t n) { + ptr_ += n; + return *this; + } + + sa_iterator& operator-=(size_t n) { + ptr_ -= n; + return *this; + } + + difference_type operator-(const sa_iterator& it) const { + return ptr_ - it.ptr_; + } + + bool operator<(const sa_iterator& it) const { + return ptr_ < it.ptr_; + } + + bool operator>(const sa_iterator& it) const { + return ptr_ > it.ptr_; + } + + bool operator<=(const sa_iterator& it) const { + return ptr_ <= it.ptr_; + } + + bool operator>=(const sa_iterator& it) const { + return ptr_ >= it.ptr_; + } + + bool operator==(const sa_iterator& it) const { + return ptr_ == it.ptr_; + } + + bool operator!=(const sa_iterator& it) const { + return ptr_ != it.ptr_; + } + + sa_iterator operator+(size_t n) const { + return sa_iterator(ptr_ + n); + } + + sa_iterator operator-(size_t n) const { + return sa_iterator(ptr_ - n); + } + + template friend sa_iterator operator+(size_t n, const sa_iterator& it); + // friend sa_iterator operator+(size_t n, const sa_iterator&); + + reference operator*() { return traits::create_reference(*ptr_); } + }; + + } + + namespace impl + { + template + class safearray_auto_ref_t; + + template + class safearray_auto_const_ref_t; + }; + + /*! \addtogroup COMType + */ + //@{ + /**STL container compatible wrapper for a safearray. + * Provides forwards and reverse iterators. + */ +#ifdef COMET_PARTIAL_SPECIALISATION + + template + struct get_extras + { + static void *extras(){ return 0; } + }; + template + struct get_extras + { + static void *extras(){ return impl::sa_traits::get_record_info().in(); } + }; + template + struct get_extras + { + static void *extras(){ return impl::sa_traits::iid().in_ptr(); } + }; + + template + struct traits_sanity_check + { static inline void check( const SAFEARRAY *psa) { } }; + template + struct traits_sanity_check + { + static void check(SAFEARRAY *psa) { + if ((psa->fFeatures & FADF_HAVEVARTYPE)!=0) + { + VARTYPE vt; + ::SafeArrayGetVartype(psa, &vt) | raise_exception ; + if(vt != impl::sa_traits::vt) + throw std::runtime_error("safearray_t: VarType mismatch"); + } + } + }; + template + struct traits_sanity_check { + static void check(SAFEARRAY *psa) + { + uuid_t iid; + ::SafeArrayGetIID(psa, &iid) | raise_exception; + if( iid != impl::sa_traits::iid() ) + throw std::runtime_error("safearray_t: IID mismatch"); + } + }; +#endif + template class safearray_t + { + public: + typedef impl::sa_traits traits; + typedef size_t size_type; ///< type for sizes (bounds etc). + typedef long index_type; ///< Type for indexing into the array + typedef ptrdiff_t difference_type; ///< Type for pointer differences + typedef typename traits::value_type value_type; ///< The type of the contained value . + typedef typename traits::reference reference; ///< Safearray reference type + typedef typename traits::const_reference const_reference; ///< Safearray const reference type + + typedef typename COMET_SAIT_ITER( safearray_t, traits::iterator, impl::sa_debug_traits ) + iterator; ///< Iterator type + typedef typename COMET_SAIT_ITER( safearray_t, traits::const_iterator, impl::sa_const_debug_traits) + const_iterator; ///< Const iterator type + +#if defined(COMET_STD_ITERATOR) + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; +#else + // workaround for broken reverse_iterator implementations due to no partial specialisation + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; +#endif + + //! \name Iterator functions + //@{ + iterator begin() { + return iterator(get_array() COMET_SAIT_THIS ); + } + + iterator end() { + return iterator(get_array() + size() COMET_SAIT_THIS ); + } + + const_iterator begin() const { + return const_iterator(get_array() COMET_SAIT_THIS) ; + } + + const_iterator end() const { + return const_iterator(get_array() + size() COMET_SAIT_THIS ); + } + + reverse_iterator rbegin() { + return reverse_iterator(end()); + } + + reverse_iterator rend() { + return reverse_iterator(begin()); + } + + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + //@} + + /// The number of elements in the array. + size_type size() const { + return psa_ ? psa_->rgsabound[0].cElements : 0; + } + + /// Returns whether the array is empty. + bool is_empty() const { + return size() == 0; + } + + /// Returns element n relative to lower_bound() + reference operator[](index_type n) { + COMET_ASSERT( (size_type)(n - lower_bound()) < size() ); + return traits::create_reference(get_element(n)); + } + + /// Returns const element n relative to lower_bound() + const_reference operator[](index_type n) const { + COMET_ASSERT( (size_type)(n - lower_bound()) < size() ); + return traits::create_reference(get_element(n)); + } + + //! Returns element n relative to lower_bound(). + /*! \throw out_of_range The index is out of range. + */ + reference at(index_type n) { + range_check(n); + return traits::create_reference(get_element(n)); + } + + //! Returns const element n relative to lower_bound(). + /*! \throw out_of_range The index is out of range. + */ + const_reference at(index_type n) const { + range_check(n); + return traits::create_reference(get_element(n)); + } + + //! The front element. + reference front() { return *begin(); } + //! The front element - const. + const_reference front() const { return *begin(); } + //! The back element. + reference back() { return *(end() - 1); } + //! The back element - const. + const_reference back() const { return *(end() - 1); } + + private: +#ifndef COMET_PARTIAL_SPECIALISATION + template + struct get_extras + { + static void *extras(){ return 0; } + }; + template<> + struct get_extras + { + static void *extras(){ return impl::sa_traits::get_record_info().in(); } + }; + template<> + struct get_extras + { + static void *extras(){ return impl::sa_traits::iid().in_ptr(); } + }; +#endif + public: + + /// \name Constructors + //@{ + /// Construct a null array. + safearray_t() : psa_(0) + {} + + /*! Attach to (and take ownership of) an existing array. + \code + SAFEARRAY *psa = SafeArrayCreateVectorEx(VT_BSTR, 0, 5, NULL); + safearray_t sa(auto_attach(psa)); + \endcode + */ + safearray_t(const impl::auto_attach_t& psa) : psa_(psa.get()) + { + sanity_check(psa_); + + if (psa_) try { + ::SafeArrayLock(psa_) | raise_exception; + } catch (...) + { + ::SafeArrayDestroy(psa_); + throw; + } + } + + /// Copy from a variant, making converting if necessary. + safearray_t(const variant_t& var) + { + if(var.get_vt() == (VT_ARRAY|traits::vt)) + { + SafeArrayCopy(var.in().parray, &psa_) | raise_exception; + } else { + variant_t v2(var, VT_ARRAY|traits::vt); + SafeArrayCopy(v2.in().parray, &psa_) | raise_exception; + } + + if (psa_) try { + ::SafeArrayLock(psa_) | raise_exception; + } catch (...) + { + ::SafeArrayDestroy(psa_); + throw; + } + + } + + /// Copy construction + safearray_t(const safearray_t& sa) + { + ::SafeArrayCopy(sa.psa_, &psa_) | raise_exception; + + if (psa_) try { + ::SafeArrayLock(psa_) | raise_exception; + } catch (...) + { + ::SafeArrayDestroy(psa_); + throw; + } + } + + /// Construct a new safearray vector. + /*! \param sz Size of the vector. + * \param lb Lower bound for the vector. + */ + explicit safearray_t(size_type sz, index_type lb) + { + if (sz > (std::numeric_limits::max)() || + sz < (std::numeric_limits::min)()) + throw std::overflow_error( + "Cannot create array of requested size"); + +#ifndef COMET_PARTIAL_SPECIALISATION + psa_ = ::SafeArrayCreateVectorEx( + traits::vt, lb, static_cast(sz), + get_extras::extras()); +#else + psa_ = ::SafeArrayCreateVectorEx( + traits::vt, lb, static_cast(sz), + get_extras::extras()); +#endif + if (psa_ == 0) throw std::bad_alloc(); + + try { + ::SafeArrayLock(psa_) | raise_exception; + } catch (...) + { + ::SafeArrayDestroy(psa_); + throw; + } + } + + /// Construct a new safearray vector. + /*! \param sz Size of the vector. + * \param lb Lower bound for the vector. + * \param val Initial value for the elements. + */ + safearray_t(size_type sz, index_type lb, const T& val) + { +#ifndef COMET_PARTIAL_SPECIALISATION + psa_ = ::SafeArrayCreateVectorEx(traits::vt, lb, sz, get_extras::extras()); +#else + psa_ = ::SafeArrayCreateVectorEx(traits::vt, lb, sz, get_extras::extras()); +#endif // COMET_PARTIAL_SPECIALISATION + if (psa_ == 0) throw std::bad_alloc(); + + try { + ::SafeArrayLock(psa_) | raise_exception; + + for (iterator it = begin(); it != end(); ++it) *it = val; + } catch (...) { + ::SafeArrayUnlock(psa_); + ::SafeArrayDestroy(psa_); + throw; + } + } + + /// Construct a safearray from an iterator, specifying the lower bound. + /** \param first First element of the container. + * \param last Beyond the last element of the container. + * \param lb Lower bound of the new safearray_t. + */ + template safearray_t(InputIterator first, InputIterator last, index_type lb) + { + initialise_aux(first, last, lb, type_traits::int_holder< type_traits::is_integer::result >()); + } + //@} + +private: + template void initialise_aux(InputIterator first, InputIterator last, index_type lb, type_traits::int_holder) + { + size_type sz = std::distance(first, last); +#ifndef COMET_PARTIAL_SPECIALISATION + psa_ = ::SafeArrayCreateVectorEx(traits::vt, lb, sz,get_extras::extras()); +#else + psa_ = ::SafeArrayCreateVectorEx(traits::vt, lb, sz,get_extras::extras()); +#endif //COMET_PARTIAL_SPECIALISATION + + if (psa_ == 0) throw std::bad_alloc(); + + try { + ::SafeArrayLock(psa_) | raise_exception; + + for (iterator it = begin(); first != last; ++first, ++it) { + *it = *first; + } + } catch (...) { + ::SafeArrayUnlock(psa_); + ::SafeArrayDestroy(psa_); + throw; + } + } + + template void initialise_aux(Integer sz, Integer lb, index_type dummy, type_traits::int_holder) + { +#ifndef COMET_PARTIAL_SPECIALISATION + psa_ = ::SafeArrayCreateVectorEx(traits::vt, lb, sz,get_extras::extras()); +#else + psa_ = ::SafeArrayCreateVectorEx(traits::vt, lb, sz,get_extras::extras()); +#endif // COMET_PARTIAL_SPECIALISATION + + if (psa_ == 0) throw std::bad_alloc(); + + ::SafeArrayLock(psa_) | raise_exception; + } + +public: + /// Resize the array, preserving lower_bound + /** If the array is null, uses 0. + */ + void resize(size_type n) + { + resize_bound(n, (psa_==NULL)?0:lower_bound()); + } + /// Resize the array, specifying the lower_bound + // This has been renamed to prevent ambiguous functions when T = size_type + void resize_bound(size_type n, size_type lb) + { + safearray_t t(n, lb); + + iterator i1 = begin(); + iterator i2 = t.begin(); + + for (;i1 != end() && i2 != t.end(); ++i1, ++i2) { + std::swap(*i1, *i2); + } + + swap(t); + } + + /** Resize the array, preserving lower_bound and specifying an initial + * value for uninitialised values. + */ + void resize( size_type n, const T& val) { + resize_bound(n,(psa_==NULL)?0:lower_bound(),val); + } + /** Resize the array, specifying lower_bound and specifying an initial + * value for uninitialised values. + */ + void resize_bound(size_type n, size_type lb, const T& val) { + safearray_t t(n, lb); + + iterator i1 = begin(); + iterator i2 = t.begin(); + + for (;i1 != end() && i2 != t.end(); ++i1, ++i2) { + std::swap(*i1, *i2); + } + + for (;i2 != t.end(); ++i2) *i2 = val; + + swap(t); + } + + /// Assign the safearray to be \p v elements of \p val. + void assign(size_type n, const T& val) { + safearray_t t(n, lower_bound(), val); + swap(t); + } + + /// Assign the safearray from a f \p first and \p last iterators. + template void assign(InputIterator first, InputIterator last) { + assign_aux(first, last, type_traits::int_holder< type_traits::is_integer::result >()); + } + + /** Return the IRecordInfo struct for the array for VT_RECORD. + */ + com_ptr get_record_info() + { + com_ptr rec_info; + ::SafeArrayGetRecordInfo(psa_, rec_info.out()) | raise_exception; + return rec_info; + } + /** Get the Variant Type of the members of the array. + */ + VARTYPE get_vt() + { + // Something appears broken in SafeArrayGetVartype - returning VT_UNKNOWN when it has FADF_DISPATCH set. + if (psa_->fFeatures & FADF_DISPATCH) return VT_DISPATCH; + if (psa_->fFeatures & FADF_UNKNOWN) return VT_UNKNOWN; + VARTYPE retval; + ::SafeArrayGetVartype(psa_, &retval) | raise_exception; + return retval; + } + + /** Return interface-id of the members of the array. + */ + uuid_t get_iid() + { + uuid_t iid; + ::SafeArrayGetIID(psa_, &iid) | raise_exception; + return iid; + } + +private: + template void assign_aux(InputIterator first, InputIterator last, type_traits::int_holder) + { + safearray_t t( first, last, lower_bound() ); + swap(t); + } + + template void assign_aux(Integer sz, const T& val, type_traits::int_holder) + { + safearray_t t(sz, lower_bound(), val); + swap(t); + } +public: + + /// Insert \p n elements of \p val at position \p pos. + void insert(iterator pos, size_type n, const T& val) { + safearray_t t(n+size(), lower_bound()); + iterator i1 = t.begin(); + iterator i2 = begin(); + + for (;i2 != pos; ++i1, ++i2) *i1 = *i2; + for (;n>0;--n, ++i1) *i1 = val; + for (;i2 != end(); ++i1, ++i2) *i1 = *i2; + + swap(t); + } + + + /// Insert elements coppied from iterator \p first to \p last at position pos. + template void insert(iterator pos, InputIterator first, InputIterator last) { + insert_aux(pos, first, last, type_traits::int_holder::result >()); + } + + /// Push an element to the back of the list (ensure lower-bound); + void push_back( const T& val, index_type lb ) + { + safearray_t t(size()+1, lb); + + iterator i1 = begin(), i2 = t.begin(); + + for (;i1 != end() ; ++i1, ++i2) + std::swap(*i1, *i2); + + *i2 = val; + + swap(t); + } + + /// Push an element to the back of the list. + inline void push_back( const T& val) + { + push_back(val, lower_bound()); + } + + /// Pop an element from the back of the list. + inline void pop_back() + { + size_type lastone = size(); + if (lastone > 0) + erase(begin()+(lastone-1)); + } + + /// Push an element to the front of the list (ensure lower-bound). + void push_front( const T &val, index_type lb) + { + safearray_t t(size()+1, lb); + + iterator i1 = begin(), i2 = t.begin(); + + *i2 = val; + + for (++i2; i1 != end(); ++i1, ++i2) + std::swap(*i1, *i2); + + swap(t); + } + /// Push an element to the front of the list. + inline void push_front( const T &val) + { + push_front(val, lower_bound()); + } + /// Pop an element from the front of the list. + inline void pop_front() + { + erase(begin()); + } + + /// Erase a specified item. + /** \param it Item to erase + * \return Item beyond erased item. + */ + iterator erase(iterator it) + { + if (it == end()) + return end(); + size_type where= it-begin(); + + safearray_t t(size()-1, lower_bound()); + iterator ret = t.end(); + iterator i1 = begin(), i2 = t.begin(); + // Copy up to iterator + for (; i1 != end() && i1 != it; ++i1, ++i2) + std::swap(*i1,*i2); + ++i1;// Skip this one + for (; i1 != end(); ++i1, ++i2) + std::swap(*i1,*i2); + + swap(t); + return begin()+where; + } + + /// Erase a range of items. + /** \param first Item to erase from + * \param second Item after range to be erased. + * \return Item beyond erased range. + */ + iterator erase(iterator first, iterator second) + { + safearray_t t(size()-(second-first), lower_bound()); + size_type where= first-begin(); + iterator i1 = begin(), i2 = t.begin(); + // Copy up to first. + for (; i1 != end() && i1 != first; ++i1, ++i2) + std::swap(*i1,*i2); + // Skip up to second + for( ; i1 != second; ++i1) + ; + // skip to end. + for (; i1 != end(); ++i1, ++i2) + std::swap(*i1,*i2); + + swap(t); + return begin()+where; + } + + + private: + template void insert_aux(iterator pos, InputIterator first, InputIterator last, type_traits::int_holder) { + size_type n = std::distance(first, last); + + safearray_t t(n+size(), lower_bound()); + iterator i1 = t.begin(); + iterator i2 = begin(); + + for (;i2 != pos; ++i1, ++i2) *i1 = *i2; + for (;first != last; ++i1, ++first) *i1 = *first; + for (;i2 != end(); ++i1, ++i2) *i1 = *i2; + + swap(t); + } + + template void insert_aux(iterator pos, Integer n, const T& val, type_traits::int_holder) { + safearray_t t(n+size(), lower_bound()); + iterator i1 = t.begin(); + iterator i2 = begin(); + + for (;i2 != pos; ++i1, ++i2) *i1 = *i2; + for (;n>0;--n, ++i1) *i1 = val; + for (;i2 != end(); ++i1, ++i2) *i1 = *i2; + + swap(t); + } + public: + + //! \name Assignment Operators + //@{ + safearray_t& operator=(const safearray_t& sa) + { + safearray_t t(sa); + swap(t); + return *this; + } + + safearray_t& operator=(const variant_t& v) + { + safearray_t t(v); + swap(t); + return *this; + } + + safearray_t& operator=(const impl::auto_attach_t& sa) + { + safearray_t t(sa); + swap(t); + return *this; + } + //@} + + private: + void destroy() { + if (psa_ != 0) { + COMET_ASSERT(psa_->cLocks == 1); + ::SafeArrayUnlock(psa_); + ::SafeArrayDestroy(psa_); + psa_ = 0; + } + } + + public: + + ~safearray_t() { + destroy(); + } + + /// Unlock and detach a raw SAFEARRAY. + SAFEARRAY* detach() { + if (psa_) { + ::SafeArrayUnlock(psa_); + } + SAFEARRAY* rv = psa_; + psa_ = 0; + return rv; + } + + /*! Detach the safearray to the variant \p var. + The safearray becomes invalid after this point. + \code + safe_array_t ints(2,0); + variant_t var; + ints.detach_to(var); + \endcode + */ + void detach_to( variant_t &var) + { + COMET_ASSERT(psa_->cLocks == 1); + if (psa_) ::SafeArrayUnlock(psa_) | raise_exception; + var = auto_attach( psa_ ); + psa_= 0; + } + + /*! Detach a safearray from the variant \p var. + An attempt is made to cast the type of the variant before attaching. + */ + void detach_from( variant_t &var) + { + if(var.get_vt()!=(VT_ARRAY|traits::vt)) + { + var.change_type(VT_ARRAY|traits::vt); + } + safearray_t t(auto_attach(var.detach().parray)); + swap(t); + } + + /// The lower bound of the array. + /** \sa get_at + */ + index_type lower_bound() const { + return psa_ ? psa_->rgsabound[0].lLbound : 0; + } + + /// Change the lower_bound of the array. + void lower_bound(index_type lb) { + psa_->rgsabound[0].lLbound = lb; + } + + private: + class sa_auto_lock_t + { + SAFEARRAY** ppsa_; + + // These are not available + sa_auto_lock_t(); + sa_auto_lock_t(const sa_auto_lock_t&); + sa_auto_lock_t& operator=(const sa_auto_lock_t&); + public: + operator SAFEARRAY**() throw() { return ppsa_; } + + sa_auto_lock_t(SAFEARRAY** ppsa) : ppsa_(ppsa) {} + + ~sa_auto_lock_t() + { + if (*ppsa_) { + HRESULT hr = ::SafeArrayLock(*ppsa_); + COMET_ASSERT( SUCCEEDED(hr) ); + hr; + } + } + }; + + public: + //! \name Access converters + //@{ + SAFEARRAY* in() const throw() { + return const_cast(psa_); + } + SAFEARRAY** in_ptr() const throw() { + return const_cast(&psa_); + } + sa_auto_lock_t inout() throw() { + if (psa_) { + ::SafeArrayUnlock(psa_); + } + return &psa_; + } + sa_auto_lock_t out() throw() { + destroy(); + return &psa_; + } + //@} + /*! Detach a raw SAFEARRAY pointer from a safearray_t. + */ + static SAFEARRAY* detach(safearray_t& sa) + { + return sa.detach(); + } + + /** Create a reference to a safearray from a raw SAFEARRAY pointer. + * Mainly used by the implementation wrappers. + */ + static const impl::safearray_auto_const_ref_t create_const_reference(SAFEARRAY* const & sa); + static impl::safearray_auto_ref_t create_reference(SAFEARRAY* & sa); + + /** Create a reference to a safearray from a variant. + \code + function( const variant_t &var) + { + const safe_array &stringarray = safe_array::create_reference( var ); + } + \endcode + */ + static const impl::safearray_auto_const_ref_t create_const_reference(const variant_t &var); + /** Create c const reference to a safearray from a variant. + */ + static impl::safearray_auto_ref_t create_reference(variant_t &var); + + void swap(safearray_t& sa) throw() + { + std::swap(psa_, sa.psa_); + } + + private: + + void range_check(index_type n) const { + size_type m = (size_type)(n - lower_bound()); + if (/*m < 0 || */ m >= size()) throw std::out_of_range("safearray_t"); + } + + typename traits::raw* get_array() const { + if (psa_) { + COMET_ASSERT(psa_->cLocks != 0); + return static_cast(psa_->pvData); + } + return NULL; + } + + typename traits::raw& get_element(size_type n) const { + return get_array()[n - lower_bound()]; + } + + protected: + SAFEARRAY* psa_; + +#ifndef COMET_PARTIAL_SPECIALISATION + + template< enum impl::sa_traits_check_type STCT > + struct traits_sanity_check + { static inline void check( const SAFEARRAY *psa) { } }; + template<> + struct traits_sanity_check + { + static void check(SAFEARRAY *psa) { + if ((psa->fFeatures & FADF_HAVEVARTYPE)!=0) + { + VARTYPE vt; + ::SafeArrayGetVartype(psa, &vt) | raise_exception ; + if(vt != impl::sa_traits::vt) + throw std::runtime_error("safearray_t: VarType mismatch"); + } + } + }; + template<> + struct traits_sanity_check { + static void check(SAFEARRAY *psa) + { + uuid_t iid; + ::SafeArrayGetIID(psa, &iid) | raise_exception; + if( iid != impl::sa_traits::iid() ) + throw std::runtime_error("safearray_t: IID mismatch"); + } + }; +#endif + /// Make sure the passed in safearray agrees with the type of the safearray_t + static void sanity_check(SAFEARRAY* psa) { + if (psa == 0) return; + if (psa->cDims != 1) throw std::runtime_error("safearray_t: Invalid dimension"); + if (!traits::are_features_ok( psa->fFeatures )) throw std::runtime_error("safearray_t: fFeatures is invalid"); +#ifndef COMET_PARTIAL_SPECIALISATION + traits_sanity_check< impl::sa_traits_check_type(traits::check_type)>::check(psa); +#else + traits_sanity_check::check(psa); +#endif + if (sizeof(T) != psa->cbElements) throw std::runtime_error("safearray_t: cbElements mismatch"); + } + + }; + //@} + + namespace impl { + + template< typename T> + class safearray_auto_ref_t : public safearray_t + { + // Don't allow any of these. + safearray_auto_ref_t(); + safearray_auto_ref_t &operator=(const safearray_auto_ref_t &); + safearray_auto_ref_t &operator=(const safearray_t &); + safearray_t& operator=(const impl::auto_attach_t &); + void swap(safearray_t& sa); + + // Remember where we got the original for a non-const reference + SAFEARRAY *& psa_; + + public: + safearray_auto_ref_t(const safearray_auto_ref_t &sa) + : safearray_t(auto_attach(sa.psa_)), psa_(sa.psa_) + { + COMET_STATIC_ASSERT(false); + } + + explicit safearray_auto_ref_t(SAFEARRAY *& psa) + : safearray_t(auto_attach(psa)), psa_(psa) + { + } + + ~safearray_auto_ref_t() + { + psa_ = this->detach(); + } + }; + + template< typename T> + class safearray_auto_const_ref_t : public safearray_t + { + // Don't allow any of these. + safearray_auto_const_ref_t(); + safearray_auto_const_ref_t &operator=(const safearray_auto_const_ref_t &); + safearray_auto_const_ref_t &operator=(const safearray_t &); + safearray_t& operator=(const impl::auto_attach_t &); + void swap(safearray_t& sa); + + public: + safearray_auto_const_ref_t(const safearray_auto_const_ref_t &sa) + : safearray_t(auto_attach(sa.psa_)) + { + COMET_STATIC_ASSERT(false); + } + + explicit safearray_auto_const_ref_t(SAFEARRAY *psa) + : safearray_t(auto_attach(psa)) + { + } + + ~safearray_auto_const_ref_t() + { + this->detach(); + } + }; + + } + + template + const impl::safearray_auto_const_ref_t safearray_t::create_const_reference(SAFEARRAY* const & sa) + { + return impl::safearray_auto_const_ref_t(sa); + } + template + impl::safearray_auto_ref_t safearray_t::create_reference(SAFEARRAY* & sa) + { + return impl::safearray_auto_ref_t(sa); + } + template + const impl::safearray_auto_const_ref_t safearray_t::create_const_reference(const variant_t &var) + { + if(var.get_vt()!=(VT_ARRAY|traits::vt)) + throw std::exception("unexepected array type"); + + SAFEARRAY *sa = var.get().parray; + return impl::safearray_auto_const_ref_t(sa); + } + template + impl::safearray_auto_ref_t safearray_t::create_reference(variant_t &var) + { + if(var.get_vt()!=(VT_ARRAY|traits::vt)) + throw std::exception("unexepected array type"); + + SAFEARRAY *sa = var.get().parray; + return impl::safearray_auto_ref_t(sa); + } + + + + template inline comet::impl::sa_iterator operator+(size_t n, const comet::impl::sa_iterator& it) { + return it + n; + } + +} // namespace comet + +namespace { + COMET_STATIC_ASSERT( sizeof(SAFEARRAY*) == sizeof(comet::safearray_t) ); +} + +namespace std { + template<> inline void swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW; + template<> inline void swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW; + template<> inline void swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW; + template<> inline void swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW; + template<> inline void swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW; + template<> inline void swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW; + template<> inline void swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW; + template<> inline void swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW; + template<> inline void swap( comet::safearray_t >& x, comet::safearray_t >& y) COMET_STD_SWAP_NOTHROW; + template<> inline void swap( comet::safearray_t >& x, comet::safearray_t >& y) COMET_STD_SWAP_NOTHROW; +} + +template<> inline void std::swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +template<> inline void std::swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +template<> inline void std::swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +template<> inline void std::swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +template<> inline void std::swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +template<> inline void std::swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +template<> inline void std::swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +template<> inline void std::swap( comet::safearray_t& x, comet::safearray_t& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +template<> inline void std::swap( comet::safearray_t >& x, comet::safearray_t >& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } +template<> inline void std::swap( comet::safearray_t >& x, comet::safearray_t >& y) COMET_STD_SWAP_NOTHROW { x.swap(y); } + +#endif diff --git a/lib/local/CamCom/comet/scope_guard.h b/lib/local/CamCom/comet/scope_guard.h new file mode 100644 index 00000000..cdb13875 --- /dev/null +++ b/lib/local/CamCom/comet/scope_guard.h @@ -0,0 +1,363 @@ +/** \file + * Scope-guards can be used to proivde transactional integrity. + * + * scope_guard and friends are adopted from source by Andrei Alexandrescu and Petru Marginean. + * + * See the
article. + */ +/* + * Copyright 2001 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + + +#ifndef COMET_SCOPE_GUARD_H +#define COMET_SCOPE_GUARD_H + +#include + +namespace comet { + + /*! \addtogroup Misc + */ + //@{ + namespace impl { + + /** Base class providing dismission and safe execution primitives. + */ + class scope_guard_impl_base + { + scope_guard_impl_base& operator =(const scope_guard_impl_base&); + protected: + ~scope_guard_impl_base() + { + } + scope_guard_impl_base(const scope_guard_impl_base& other) throw() + : dismissed_(other.dismissed_) + { + other.dismiss(); + } + template static void safe_execute(J& j) throw() + { + if (!j.dismissed_) + try + { + j.execute(); + } + catch(...) + { + } + } + + mutable bool dismissed_; + public: + scope_guard_impl_base() throw() : dismissed_(false) + { + } + void dismiss() const throw() + { + dismissed_ = true; + } + }; + + /** 0 parameter scope guard. + * \internal + */ + template class scope_guard_impl_0 : public scope_guard_impl_base + { + public: + ~scope_guard_impl_0() throw() + { + safe_execute(*this); + } + + void execute() + { + fun_(); + } + + scope_guard_impl_0(F fun) : fun_(fun) + {} + + private: + F fun_; + }; + + /** 1 parameter scope guard. + * \internal + */ + template class scope_guard_impl_1 : public scope_guard_impl_base + { + public: + ~scope_guard_impl_1() throw() + { + safe_execute(*this); + } + + void execute() + { + fun_(p1_); + } + + scope_guard_impl_1(F fun, P1 p1) : fun_(fun), p1_(p1) + {} + + private: + F fun_; + const P1 p1_; + }; + + /** 2 parameter scope guard. + * \internal + */ + template class scope_guard_impl_2: public scope_guard_impl_base + { + public: + ~scope_guard_impl_2() throw() + { + safe_execute(*this); + } + + void execute() + { + fun_(p1_, p2_); + } + + scope_guard_impl_2(F fun, P1 p1, P2 p2) : fun_(fun), p1_(p1), p2_(p2) + {} + + private: + F fun_; + const P1 p1_; + const P2 p2_; + }; + + /** 3 parameter scope guard. + * \internal + */ + template class scope_guard_impl_3 : public scope_guard_impl_base + { + public: + ~scope_guard_impl_3() throw() + { + safe_execute(*this); + } + + void execute() + { + fun_(p1_, p2_, p3_); + } + + scope_guard_impl_3(F fun, P1 p1, P2 p2, P3 p3) : fun_(fun), p1_(p1), p2_(p2), p3_(p3) + {} + + private: + F fun_; + const P1 p1_; + const P2 p2_; + const P3 p3_; + }; + + /** 0 parameter object scope guard. + * \internal + */ + template class obj_scope_guard_impl_0 : public scope_guard_impl_base + { + public: + ~obj_scope_guard_impl_0() throw() + { + safe_execute(*this); + } + + void execute() + { + (obj_.*memFun_)(); + } + + obj_scope_guard_impl_0(Obj& obj, MemFun memFun) + : obj_(obj), memFun_(memFun) {} + + private: + Obj& obj_; + MemFun memFun_; + }; + + /** 1 parameter object scope guard. + * \internal + */ + template class obj_scope_guard_impl_1 : public scope_guard_impl_base + { + public: + ~obj_scope_guard_impl_1() throw() + { + safe_execute(*this); + } + + void execute() + { + (obj_.*memFun_)(p1_); + } + + obj_scope_guard_impl_1(Obj& obj, MemFun memFun, P1 p1) + : obj_(obj), memFun_(memFun), p1_(p1) {} + + private: + Obj& obj_; + MemFun memFun_; + const P1 p1_; + }; + + /** 2 parameter object scope guard. + * \internal + */ + template class obj_scope_guard_impl_2 : public scope_guard_impl_base + { + public: + ~obj_scope_guard_impl_2() throw() + { + safe_execute(*this); + } + + void execute() + { + (obj_.*memFun_)(p1_, p2_); + } + + obj_scope_guard_impl_2(Obj& obj, MemFun memFun, P1 p1, P2 p2) + : obj_(obj), memFun_(memFun), p1_(p1), p2_(p2) {} + + private: + Obj& obj_; + MemFun memFun_; + const P1 p1_; + const P2 p2_; + }; + + /** Implementation to allow argument to be passed by reference. + * \internal + * \sa make_guard + */ + template class ref_holder + { + T& ref_; + public: + ref_holder(T& ref) : ref_(ref) {} + operator T& () const + { + return ref_; + } + private: + // Disable assignment - not implemented + ref_holder& operator=(const ref_holder&); + }; + + } + + /** Allow an argument to be passed by reference. + * \code + * scope_guard guard = make_guard( fun, by_ref(long) ); + * \endcode + * \relates comet::scope_guard + * \internal + */ + template inline impl::ref_holder by_ref(T& t) + { + return impl::ref_holder(t); + } + + /** Implements a scope guard with 0 parameters. + * \param fun Function or \link functor comet::functor \endlink + * \relates comet::scope_guard + * \internal + */ + template inline impl::scope_guard_impl_0 make_guard(F fun) + { + return impl::scope_guard_impl_0(fun); + } + + /** Implements a scope guard with 1 parameter. + * \param fun Function or \link functor comet::functor \endlink + * \relates comet::scope_guard + * \sa by_ref + * \internal + */ + template inline impl::scope_guard_impl_1 make_guard(F fun, P1 p1) + { + return impl::scope_guard_impl_1(fun, p1); + } + + /** Implements a scope guard with 2 parameters. + * \param fun Function or \link functor comet::functor \endlink + * \relates comet::scope_guard + * \sa by_ref + * \internal + */ + template inline impl::scope_guard_impl_2 make_guard(F fun, P1 p1, P2 p2) + { + return impl::scope_guard_impl_2(fun, p1, p2); + } + + /** Implements a scope guard with 3 parameters. + * \param fun Function or \link functor comet::functor \endlink + * \relates comet::scope_guard + * \sa by_ref + * \internal + */ + template inline impl::scope_guard_impl_3 make_guard(F fun, P1 p1, P2 p2, P3 p3) + { + return impl::scope_guard_impl_3(fun, p1, p2, p3); + } + + /** Implements a scope guard with 0 parameters, calling a memeber function on an object. + * \param fun Function or \link functor comet::functor \endlink + * \relates comet::scope_guard + */ + template inline impl::obj_scope_guard_impl_0 make_obj_guard(Obj& obj, MemFun memFun) + { + return impl::obj_scope_guard_impl_0(obj, memFun); + } + + /** Implements a scope guard with 1 parameter, calling a memeber function on an object. + * \param fun Function or \link functor comet::functor \endlink + * \relates comet::scope_guard + * \sa by_ref + * \internal + */ + template inline impl::obj_scope_guard_impl_1 make_obj_guard(Obj& obj, MemFun memFun, P1 p1) + { + return impl::obj_scope_guard_impl_1(obj, memFun, p1); + } + + /** Implements a scope guard with 2 parameters, calling a memeber function on an object. + * \param fun Function or \link functor comet::functor \endlink + * \relates comet::scope_guard + * \sa by_ref + * \internal + */ + template inline impl::obj_scope_guard_impl_2 make_obj_guard(Obj& obj, MemFun memFun, P1 p1, P2 p2) + { + return impl::obj_scope_guard_impl_2(obj, memFun, p1, p2); + } + + /** Pointer to a scope guard. + * Relies on const references holding on to an assigned stack object for + * the scope of the reference. + * \sa scope_guard_impl_0 obj_scope_guard_impl_0 scope_guard_impl_1 obj_scope_guard_impl_1 scope_guard_impl_2 obj_scope_guard_impl_2 + */ + typedef const impl::scope_guard_impl_base& scope_guard; + + //@} + +} // namespace + +#endif diff --git a/lib/local/CamCom/comet/server.h b/lib/local/CamCom/comet/server.h new file mode 100644 index 00000000..3d877863 --- /dev/null +++ b/lib/local/CamCom/comet/server.h @@ -0,0 +1,1588 @@ +/** \file + * Main functionality for providing a COM server dll. + */ +/* + * Copyright 2000-2002 Sofus Mortensen, Paul Hollingsworth, Michael Geddes, Mikael Lindgren + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_SERVER_H +#define COMET_SERVER_H + +#include +#ifdef COMET_GCC_HEADERS +#include +#else // COMET_GCC_HEADERS +#include +#endif // COMET_GCC_HEADERS + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/** \page cometclassfactory Comet Class Factories + * Comet currently has support for \ref cometclassfactorystandard (non-aggregating), \ref cometclassfactoryaggregating and + * \ref cometclassfactorysingleton class factories. As there has been no demand for custom + * class factories yet, there is currently no support for them. + * + * The different class-factories are enabled by specialising the + * coclass_implementation to inherit off different classes. + * + * \section cometclassfactorystandard Standard + * + * The trigger classes for standard class-factory are either comet::coclass, or + * comet::simple_object. + * + * There is nothing much more noteworthy about this form except that it will + * return CLASS_E_NOAGGREGATION if aggregation is specified. + * + * \section cometclassfactoryaggregating Aggregating + * + * The trigger classes for the aggregating class-factory are comet::aggregateable_coclass or + * comet::aggregateable_object. + * + * The implementation of aggregation in comet is similar to the + * poly-aggregateable implementations found in ATL; there is no + * aggregation-only coclass implementation. + * + * The aggregating class factory will hook up aggegation if an IUnknown is + * supplied. + * + * \section cometclassfactorysingleton Singleton + * + * The trigger classes for the singleton class-factory are + * comet::singleton_coclass or comet::singleton_object. + * + * The singleton class-factory will cause all CreateObject calls for this class to + * return the same object. The life-time of the object is from first call till + * just before the dll/exe is unloaded. + * + * More complex requirements are anticipated, however they have not been + * implemented. + */ + /*! \defgroup Server Server implementation details. + */ + //@{ + +/** Template class for specifying your own custom registration. + + The default template does nothing - define a specialization of ::custom_registration + for each Coclass that you wish to have custom registration. + + on_register is called \em after the standard registration. + on_unregister is called \em before the standard unregistration. + + Common initialization and cleanup code for both registration and unregistration + can be put in the constructor and destructor. + + All exceptions thrown by on_unregister are caught and discarded. + + Example usage: + \code + class custom_registration + { + comet::regkey key_; + public: + custom_registration() + { + key_ = comet::regkey(HKEY_LOCAL_MACHINE).open(_T("Software\\AcmeCorp\\AcmePayroll")); + }; + void on_register(const TCHAR *filename) + { + key_.create(_T("Bill Bloggs")); + } + void on_unregister(const TCHAR *filename) + { + key_.delete_subkey(_T("Bill Bloggs")); + } + }; + \endcode +*/ +template +class custom_registration +{ +public: + void on_register(const TCHAR *) {} + void on_unregister(const TCHAR *) {} +}; +//@} + +namespace comet { + + + namespace impl { + + enum factory_type_t { ft_standard, ft_aggregateable, ft_singleton }; + + inline void create_record_info( const IID& lib_guid, const IID& rec_guid, unsigned short major_version, unsigned short minor_version, IRecordInfo*& ri ) + { + auto_cs lock( module().cs() ); + + if (!ri) { + com_ptr tl; + LoadRegTypeLib(lib_guid, + major_version, + minor_version, + GetUserDefaultLCID(), + tl.out()) | raise_exception; + com_ptr ti; + tl->GetTypeInfoOfGuid(rec_guid, ti.out()) | raise_exception; + GetRecordInfoFromTypeInfo(ti.in(), &ri) | raise_exception; + module().add_object_to_dispose( impl::create_itf_releaser( ri ) ); + } + } + + template struct interface_wrapper : public T + { + typedef T interface_is; + }; + + template class ATL_NO_VTABLE simple_object_aux : + public implement_qi< typelist::append< T, + make_list >::result > > + { + public: +// enum { factory_type = ft_standard }; + + STDMETHOD_(ULONG, AddRef)() + { + LONG rc = InterlockedIncrement(&rc_); + if (rc_ == 1) module().lock(); + return rc; + } + + STDMETHOD_(ULONG, Release)() + { + LONG rc = InterlockedDecrement(&rc_); + if (rc == 0) { + try { + delete this; + } COMET_CATCH_UNKNOWN( L"Release", IID_IUnknown, bstr_t()); + module().unlock(); + } + return rc; + } + + STDMETHOD(InterfaceSupportsErrorInfo)(REFIID) + { + return S_OK; + } + + void so_internal_addref() { ++rc_; } + void so_internal_release() { --rc_; } + + protected: + simple_object_aux() : rc_(0) {} + virtual ~simple_object_aux() {} + private: + long rc_; + + // non-copyable + simple_object_aux(const simple_object_aux&); + simple_object_aux& operator=(const simple_object_aux&); + }; + + } + /*!\defgroup Objects Classes used as bases for COM objects. + */ + //@{ + /** Provide an inner unknown for aggregation. + * This is the unknown that handles lifetime of an aggregateable object. + */ + template + struct aggregate_inner_unknown : IUnknown + { + aggregate_inner_unknown() : rc_(0) {} + + STDMETHOD_(ULONG, AddRef)() + { + if (rc_ == 0) module().lock(); + return InterlockedIncrement(&rc_); + } + + STDMETHOD_(ULONG, Release)() + { + size_t rc = InterlockedDecrement(&rc_); + if (rc == 0) { + try { + delete static_cast(this); + } + COMET_CATCH_UNKNOWN( L"Release", IID_IUnknown, bstr_t()); + module().unlock(); + } + return rc; + } + + STDMETHOD(QueryInterface)(REFIID riid, void **pv) + { + if(riid == IID_IUnknown) + { + *pv=get_inner(); + AddRef(); + return S_OK; + } + return static_cast(this)->QueryInterfaceInternal(riid, pv); + } + + IUnknown *get_inner() { return static_cast(this); } + + private: + long rc_; + }; + + /** Provides the outer-unknown for aggregation. + * This unkonwn points to the agregating unknown if aggregation occured, or + * to the aggregate_inner_unknown class if it didn't. + * This also implements the interfaces and QueryInterface. + */ + template + class aggregate_outer_unknown : public implement_internal_qi< + typelist::append >::result > + > + { + public: + aggregate_outer_unknown(): outer_(NULL) {} + + STDMETHOD_(ULONG, AddRef)() + { + //assert(outer_!=NULL); + return outer_->AddRef(); + } + + STDMETHOD_(ULONG, Release)() + { + //assert(outer_!=NULL); + return outer_->Release(); + } + STDMETHOD(QueryInterface)(REFIID riid, void** ppv) + { + //assert(outer_!=NULL); + return outer_->QueryInterface(riid, ppv); + } + STDMETHOD(InterfaceSupportsErrorInfo)(REFIID) + { + return S_OK; + } + void set_outer_(IUnknown *outer){ outer_ = outer;} + + + private: + IUnknown *outer_; + }; + //@} + + namespace impl { + + template class ATL_NO_VTABLE aggregateable_object_aux : public aggregate_outer_unknown, public aggregate_inner_unknown > + { + public: + aggregateable_object_aux() + { + set_outer_(static_cast(static_cast< aggregate_inner_unknown > *>(this))); + } + + protected: + virtual ~aggregateable_object_aux() {} + friend struct aggregate_inner_unknown >; + private: + // non-copyable + aggregateable_object_aux(const aggregateable_object_aux&); + aggregateable_object_aux& operator=(const aggregateable_object_aux&); + }; + + } + + /*!\addtogroup Objects + */ + //@{ + /*! \class aggregateable_object server.h comet/server.h + Implement a user aggregateable object. + \code + class my_class : public aggregateable_object< IFooImpl, IBarImpl > + { + ... + }; + \endcode + \sa handle_exception_default IProvideClassInfoImpl simple_object + */ + template class ATL_NO_VTABLE aggregateable_object : public impl::aggregateable_object_aux< typename make_list::result > + { + public: + enum { factory_type = impl::ft_aggregateable }; + }; + + /*! \class simple_object server.h comet/server.h + A simple reference counted COM object. + \code + class my_class : public simple_object< IFooImpl, IBarImpl > + { + ... + }; + \endcode + \sa handle_exception_default IProvideClassInfoImpl aggregateable_object + */ + template class ATL_NO_VTABLE simple_object : public impl::simple_object_aux< typename make_list::result > + { + public: + enum { factory_type = impl::ft_standard }; + }; + //@} + + namespace impl { + + + + template class ATL_NO_VTABLE static_object_aux : + public implement_qi< typelist::append< T, make_list >::result > > + { + public: + STDMETHOD_(ULONG, AddRef)() + { + module().lock(); + return 2; + } + + STDMETHOD_(ULONG, Release)() + { + module().unlock(); + return 1; + } + + STDMETHOD(InterfaceSupportsErrorInfo)(REFIID) + { + return S_OK; + } + protected: + static_object_aux() {} + virtual ~static_object_aux() {} + }; + + } + + /*!\addtogroup Objects + */ + //@{ + /*! \class static_object server.h comet/server.h + A simple static allocated COM object. + \code + class my_class : public static_object< IFooImpl, IBarImpl > + { + ... + }; + \endcode + * \sa handle_exception_default IProvideClassInfoImpl + */ + template class ATL_NO_VTABLE static_object : public impl::static_object_aux< typename make_list::result > + { + }; + + /*! \class singleton_object server.h comet/server.h + A simple singleton allocated COM object. + \code + class my_class : public singleton_object< IFooImpl, IBarImpl > + { + ... + }; + \endcode + * \sa handle_exception_default IProvideClassInfoImpl + */ + template class ATL_NO_VTABLE singleton_object : public impl::static_object_aux< typename make_list::result > + { + public: + enum { factory_type = impl::ft_singleton }; + void set_dispose_command_( impl::cmd_t *) { } + }; + + + /*! \class embedded_object server.h comet/server.h + * + * \sa handle_exception_default IProvideClassInfoImpl + * \todo documentation + */ + template class ATL_NO_VTABLE embedded_object : + public implement_qi< typelist::append< typename make_list::result, typename make_list >::result > > + { + public: + STDMETHOD_(ULONG, AddRef)() + { + return parent_->AddRef(); + } + + STDMETHOD_(ULONG, Release)() + { + return parent_->Release(); + } + + STDMETHOD(InterfaceSupportsErrorInfo)(REFIID) + { + return S_OK; + } + + protected: + explicit embedded_object(PARENT* parent) : parent_(parent) {} + + PARENT* get_parent() const { return parent_; } + + typedef embedded_object base_class; + private: + PARENT* parent_; + + // non-copyable + embedded_object(const embedded_object&); + embedded_object& operator=(const embedded_object&); + }; + + /*! \class embedded_object2 server.h comet/server.h + * + * \sa handle_exception_default IProvideClassInfoImpl + * \todo documentation + */ + template class ATL_NO_VTABLE embedded_object2 : + public implement_qi< typelist::append< typename make_list::result, make_list >::result > > + { + public: + STDMETHOD_(ULONG, AddRef)() + { + if (rc_ == 0) module().lock(); + long r = InterlockedIncrement(&rc_); + if (is_connected_) return parent_->AddRef(); + return r; + } + + STDMETHOD_(ULONG, Release)() + { + size_t rc = InterlockedDecrement(&rc_); + + if (is_connected_) return parent_->Release(); + + if (rc == 0) { + try { + delete this; + } + COMET_CATCH_UNKNOWN( L"Release", IID_IUnknown, bstr_t()); + module().unlock(); + } + return rc; + } + + STDMETHOD(InterfaceSupportsErrorInfo)(REFIID) + { + return S_OK; + } + + void disconnect() + { + // todo make thread safe! + + if (is_connected_) { + is_connected_ = false; + + for (long i=0; iRelease(); + + // Provoke destruction if necessary. + AddRef(); + Release(); + } + } + + protected: + explicit embedded_object2(PARENT* parent) : parent_(parent), rc_(0), is_connected_(true) {} + + virtual ~embedded_object2() {} + + PARENT* get_parent() const { return parent_; } + + typedef embedded_object2 base_class; + private: + PARENT* parent_; + bool is_connected_; + long rc_; + + // non-copyable + embedded_object2(const embedded_object2&); + embedded_object2& operator=(const embedded_object2&); + }; + //@} + + /// Base class for class factories. + template class class_factory_base : public IClassFactory + { + public: + STDMETHOD_(ULONG, AddRef)() + { + if (LOCK_MODULE) module().lock(); + return 2; + } + + STDMETHOD_(ULONG, Release)() + { + if (LOCK_MODULE) module().unlock(); + return 1; + } + + STDMETHOD(QueryInterface)(REFIID riid, void **pv) + { + if (!pv) return E_POINTER; + *pv = 0; + if (riid == IID_IClassFactory) *pv = static_cast(this); + if (riid == IID_IUnknown) *pv = this; + + if (*pv == NULL) return E_NOINTERFACE; + + AddRef(); + return S_OK; + } + + STDMETHOD(LockServer)(BOOL bLock) + { + if (bLock) + module().lock(); + else + module().unlock(); + return S_OK; + } + protected: + HRESULT handle_exception( const bstr_t &src ) + { +#ifndef COMET_DISABLE_EXCEPTION_RETHROW_CATCH + return comet_exception_handler::rethrow( source_info_t( src, IID_IUnknown, L"ClassFactory") ); +#else // COMET_DISABLE_EXCEPTION_RETHROW_CATCH + return comet_exception_handler::catcher_hr(E_FAIL, source_info_t(src, IID_IUnknown, L"ClassFactory") ); +#endif // COMET_DISABLE_EXCEPTION_RETHROW_CATCH + } + }; + + //! Basic class-factory. + template class class_factory : public class_factory_base + { + public: + STDMETHOD(CreateInstance)(::IUnknown *pUnkOuter, REFIID riid, void **ppv) + { + if (pUnkOuter) return CLASS_E_NOAGGREGATION; + *ppv = 0; + + if (!LOCK_MODULE) module().lock(); + + T::coclass_type* t; + try { + t = new T; + } + catch (...) + { + if (!LOCK_MODULE) module().unlock(); + return handle_exception( bstr_t(L"CreateInstance(") + bstr_t(uuid_t::create_const_reference(riid),true ) + ")" ); + } + + t->AddRef(); + HRESULT hr = t->QueryInterface(riid, ppv); + t->Release(); + + if (!LOCK_MODULE) module().unlock(); + + return hr; + } + + }; + + //! Class factory for aggregateable objects. + template class class_factory_agg : public class_factory_base + { + STDMETHOD(CreateInstance)(::IUnknown *pUnkOuter, REFIID riid, void **ppv) + { + *ppv = 0; + + if (!LOCK_MODULE) module().lock(); + + T* t; + try { + t = new T; + } + catch (...) + { + if (!LOCK_MODULE) module().unlock(); + return handle_exception( bstr_t(L"CreateInstance(") + bstr_t(uuid_t::create_const_reference(riid),true ) + ")" ); + } + if(pUnkOuter!=NULL) + { + if(riid!=IID_IUnknown) + { + if (!LOCK_MODULE) module().unlock(); + return CLASS_E_NOAGGREGATION; + } + t->set_outer_(pUnkOuter); + } + + t->get_inner()->AddRef(); + HRESULT hr = t->get_inner()->QueryInterface(riid, ppv); + t->get_inner()->Release(); + + if (!LOCK_MODULE) module().unlock(); + + return hr; + } + }; + + /// Class factory for singletons. + template class class_factory_singleton : public class_factory_base + { + public: + class_factory_singleton() : obj_(NULL), primed_(0) + { } + + STDMETHOD(CreateInstance)(::IUnknown *pUnkOuter, REFIID riid, void **ppv) + { + *ppv = 0; + if(pUnkOuter!=NULL) + return CLASS_E_NOAGGREGATION; + + if (!LOCK_MODULE) module().lock(); + + // Add this to the list to get the object disposed on + // moudle terminate. + if (0==InterlockedExchange(&primed_, 1)) + { + module().add_object_to_dispose( create_object_disposer(this) ); + } + + if (obj_ == NULL ) + { + + try { + // Create a new instance, but make sure only one is + // used if there is contention. + T *newVal = new T; + newVal->set_dispose_command_( create_object_disposer(this)); +#ifndef InterlockedCompareExchangePointer + InterlockedCompareExchange( &(void *&)obj_, newVal, 0); +#else + InterlockedCompareExchangePointer( &(void *&)obj_, newVal, 0); +#endif + } + catch (...) + { + if (!LOCK_MODULE) module().unlock(); + return handle_exception( bstr_t(L"CreateInstance(") + bstr_t(uuid_t::create_const_reference(riid),true ) + ")" ); + } + } + + HRESULT hr = obj_->QueryInterface(riid, ppv); + + if (!LOCK_MODULE) module().unlock(); + + return hr; + } + + // Called by object_disposer on module terminate. + void object_dispose() + { + if (obj_ != NULL) + { + T *t= obj_; + obj_ = NULL; + CoDisconnectObject(t->get_unknown(), 0); + delete t; + } + } + + T *obj_; + long primed_; + private: + }; + + + /*!\addtogroup Objects + */ + //@{ + /** Enumerate thread_model. + */ + namespace thread_model { + /** Enumerate thread_model. + */ + enum thread_model_t { + Apartment = 0, ///< Apartment threaded. + Free = 1, ///< Free threaded. + Both = 2, ///< Both threaded (either apartment or free) + Neutral = 3 ///< Netural threaded. + }; + } + //@} + + //! Provide static string description of thread models. + template + struct tm_properties; + + template<> struct tm_properties + { + COMET_FORCEINLINE static const TCHAR *string() { return _T("Apartment"); } + }; + + template<> struct tm_properties + { + COMET_FORCEINLINE static const TCHAR *string() { return _T("Free"); } + }; + + template<> struct tm_properties + { + COMET_FORCEINLINE static const TCHAR *string() { return _T("Both"); } + }; + + template<> struct tm_properties + { + COMET_FORCEINLINE static const TCHAR *string() { return _T("Neutral"); } + }; + + /*! \defgroup Interfaces Interface implementations. + */ + //@{ + /*! \class IProvideClassInfoImpl server.h comet/server.h + * This class provides IProvideClassInfo interfaces for simple, static, embeded or + * other custom classes. + * IProvideClassInfoImpl is used by coclass and aggregateable_coclass by + * default. + * This class also provides class name information to the exception + * handler. + * \sa default_exception_handler_traits + */ + template class IProvideClassInfoImpl : public IProvideClassInfo, public handle_exception_default + { + public: + typedef IProvideClassInfo interface_is; + STDMETHOD(GetClassInfo)(ITypeInfo **ppTypeInfo) throw() + { + if(!ppTypeInfo) return E_POINTER; + *ppTypeInfo = 0; + ITypeLib *pTypeLib; + typedef typename COCLASS::type_library COCLASS_typelib; + HRESULT hr = typelibrary_loader::load(&pTypeLib); + + if(FAILED(hr)) return hr; + hr = pTypeLib->GetTypeInfoOfGuid(uuidof(), ppTypeInfo); + pTypeLib->Release(); + return hr; + } + }; + /** \struct coclass server.h comet/server.h + * Implement a standard coclass with interfaces defined in TypeLibrary and + * implemented within the class \p T and thread model \p TM, followed by a + * list of extra interfaces to implement. + * Provides IProvideClassInfo and ISupportErrorInfo as standard. + * \code + template<> + class coclass_implementation : public coclass + { + // ... + }; + * \endcode + * The thread model can be specified for the coclass, and as well, extra + * interfaces can be implemented by specifying them at the end. + * \code + template<> + class coclass_implementation : public coclass + { + // ... + }; + * \endcode + * \sa FTM aggregates thread_model::thread_model_t + */ + +/* template struct ATL_NO_VTABLE coclass : public impl::simple_object_aux< typelist::append >::result > > { + enum { thread_model = TM }; + static const TCHAR* get_progid() { return 0; } + };*/ + //@} + + /*!\addtogroup Objects + */ + //@{ + template struct ATL_NO_VTABLE coclass : public impl::simple_object_aux< typelist::append< typelist::append< typename T::interface_impls, typename make_list::result>, typename make_list >::result > > { + typedef coclass coclass_type; + enum { factory_type = impl::ft_standard }; + enum { thread_model = TM }; + static const TCHAR* get_progid() { return 0; } + }; + //@} + +// COMET_WRAP_EACH_DECLARE(aggregates_interface); + + namespace impl { + template + struct append3 + { + typedef typelist::append first_two; + typedef typelist::append list; + }; + } +#ifdef NOTNOW + /** \struct coclass_aggregates server.h comet/server.h + * Implement a coclass with interfaces defined in TypeLibrary and + * implemented by the class, as well as supporting specified aggregated interfaces. + * Provides IProvideClassInfo and ISupportErrorInfo as standard. + * \code + * template<> + * class coclass_implementation + * : public coclass_aggregates::result> + * { + * public: + * coclass_implementation() + * { + * aggregates_interface::set_aggregate(com_ptr(CoClassForAggIFace,com_cast(this))); + * } + * // ... + * }; + * \endcode + * \note This should provide IProvideMultipleClassInfo. + */ + +/* template + struct ATL_NO_VTABLE coclass_aggregates : public impl::simple_object_aux >::result > > + { + enum { thread_model = TM }; + static const TCHAR* get_progid() { return 0; } + };*/ +#endif //0 + + /*!\addtogroup Objects + */ + //@{ + /** \struct aggregateable_coclass server.h comet/server.h + * Implement an aggregateable coclass with interfaces defined in TypeLibrary and + * implemented within the class T. + * Provides IProvideClassInfo and ISupportErrorInfo as standard. + * \code + * template<> + * class coclass_implementation + * : public aggregateable_coclass + * { + * // .... + * }; + * \endcode + */ + + template + struct ATL_NO_VTABLE aggregateable_coclass : public impl::aggregateable_object_aux< typelist::append< typename T::interface_impls, typename make_list >::result > > + { + typedef aggregateable_coclass coclass_type; + enum { thread_model = TM }; + enum { factory_type = impl::ft_aggregateable }; + static const TCHAR* get_progid() { return 0; } + aggregateable_coclass() {} + protected: + ~aggregateable_coclass() {} + private: + aggregateable_coclass(const aggregateable_coclass&); + aggregateable_coclass& operator=(const aggregateable_coclass&); + }; + + /** \class singleton_coclass server.h comet/server.h + * Implement a singleton coclass within interfaces defined in TypeLibrary + * and implemented with the class T. + * Gets cleaned up when dll unloads. + * Provides IProvideClassInfo and ISupportErrorInfo as standard. + \code + class coclass_implementation + : public singleton_coclass + { + // .... + }; + \endcode + */ + template< typename T, enum thread_model::thread_model_t TM = thread_model::Apartment> + struct ATL_NO_VTABLE singleton_coclass : public impl::static_object_aux< + typelist::append< typename T::interface_impls, typename make_list >::result > + > + { + typedef singleton_coclass coclass_type; + enum { thread_model = TM }; + enum { factory_type = impl::ft_singleton }; + void set_dispose_command_( impl::cmd_t *) { } + static const TCHAR* get_progid() { return 0; } + singleton_coclass() {} + ~singleton_coclass() {} + + private: + singleton_coclass(const singleton_coclass&); + singleton_coclass& operator=(const singleton_coclass&); + }; + + template< typename T, enum thread_model::thread_model_t TM = thread_model::Apartment> + struct ATL_NO_VTABLE singleton_autorelease_coclass : public impl::static_object_aux< + typelist::append< typename T::interface_impls, typename make_list >::result > + > + { + singleton_autorelease_coclass() : rc_(0), dispose_(0) {} + ~singleton_autorelease_coclass() + { delete dispose_; } + + STDMETHOD_(ULONG, AddRef)() + { + if (rc_ == 0) module().lock(); + return InterlockedIncrement(&rc_); + } + + STDMETHOD_(ULONG, Release)() + { + LONG rc = InterlockedDecrement(&rc_); + if (rc == 0) { + try { + if (dispose_!=NULL) + dispose_->cmd(); + + } COMET_CATCH_UNKNOWN( L"Release", IID_IUnknown, bstr_t()); + module().unlock(); + } + return rc; + } + + typedef singleton_autorelease_coclass coclass_type; + enum { thread_model = TM }; + enum { factory_type = impl::ft_singleton }; + static const TCHAR* get_progid() { return 0; } + + void set_dispose_command_( impl::cmd_t *p) + { + delete dispose_; + dispose_ = p; + } + + private: + singleton_autorelease_coclass(const singleton_autorelease_coclass&); + singleton_autorelease_coclass& operator=(const singleton_autorelease_coclass&); + long rc_; + impl::cmd_t *dispose_; + }; + //@} + + + namespace impl { + template + class reghelper_t + { + // convert_string is overloaded to select the correct + // behaviour based on argument type. + // dest_size is the number of bytes, not the number of characters. + COMET_FORCEINLINE static void convert_string(wchar_t *dest, size_t dest_size, const wchar_t *src) + { + ::memcpy(dest, src, dest_size); + } + + COMET_FORCEINLINE static void convert_string(char *dest, size_t dest_size, const wchar_t *src) + { + ::WideCharToMultiByte(CP_ACP, 0, src, -1, dest, dest_size, 0, 0); + } + + static void removekey(const tstring& key); + static void addkey(const tstring& key, const tstring& valueName, const tstring& value); + static void addkey(const tstring& key, const tstring& value); + static tstring StringFromUUID(REFCLSID rclsid); + static void updatekey(bool unregister, const tstring &key, const tstring &valueName, const tstring &value); + static void updatekey(bool unregister, const tstring &key, const tstring &value); + public: + static void update_coclass(bool unregister, + const CLSID &rclsid, + const TCHAR *filename, + const TCHAR *thread_model, + const TCHAR *coclass_name, + const TCHAR *progid, + unsigned long version, + const GUID &rlibid, + bool inproc_server, + const GUID* appid); + }; // reghelper_t + + template + void reghelper_t::removekey(const tstring& key) + { + regkey rkey(HKEY_CLASSES_ROOT); + rkey.delete_subkey_nothrow(key); + } + + template + void reghelper_t::addkey(const tstring& key, const tstring& valueName, const tstring& value) + { + regkey rkey(HKEY_CLASSES_ROOT); + rkey.create(key)[valueName] = value; + } + + template + void reghelper_t::addkey(const tstring& key, const tstring& value) + { + regkey rkey(HKEY_CLASSES_ROOT); + rkey.create(key)[_T("")] = value; + } + + template + tstring reghelper_t::StringFromUUID(REFCLSID rclsid) + { + wchar_t *ws; + ::StringFromCLSID(rclsid, &ws); + size_t num_chars = wcslen(ws) + 1; + size_t bytes = num_chars * sizeof (TCHAR); + TCHAR *s = static_cast(_alloca(bytes)); + convert_string(s, bytes, ws); + CoTaskMemFree(ws); + return s; + } + + template + void reghelper_t::updatekey(bool unregister, const tstring &key, const tstring &valueName, const tstring &value) + { + if(unregister) + removekey(key); + else + addkey(key, valueName, value); + } + + template + void reghelper_t::updatekey(bool unregister, const tstring &key, const tstring &value) + { + if(unregister) + removekey(key); + else + addkey(key, value); + } + + template + void reghelper_t::update_coclass(bool unregister, + const CLSID &rclsid, + const TCHAR *filename, + const TCHAR *thread_model, + const TCHAR *coclass_name, + const TCHAR *progid, + unsigned long version, + const GUID &rlibid, + bool inproc_server, + const GUID* /*appid*/) + { + tstring clsid = StringFromUUID(rclsid); + tstring name = _T("CLSID\\") + clsid; + tstring typelib = StringFromUUID(rlibid); + // On WinNT/Win2000, subkeys must be deleted before parent keys. + // Therefore, be sure to specify changes to subkeys before + // changes to parent keys - otherwise the server will not + // cleanly remove all keys when it is unregistered. + if (inproc_server) + { + updatekey(unregister, name + _T("\\InprocServer32"), filename); + updatekey(unregister, name + _T("\\InprocServer32"), _T("ThreadingModel"), thread_model); + } + else + { + updatekey(unregister, name + _T("\\LocalServer32"), filename); + updatekey(unregister, name + _T("\\Programmable"), _T("")); + } + updatekey(unregister, name + _T("\\TypeLib"), typelib); +// updatekey(unregister, name, coclass_name); + + if (progid != 0) + { + tstring prgid(progid); + + if (version != 0) + { + TCHAR buffer[35]; +#if _MSC_VER >= 1400 + _ultot_s(version, buffer, 35, 10); + tstring prgid_ver(prgid + _T(".") + buffer); +#else + tstring prgid_ver(prgid + _T(".") + _ultot(version, buffer, 10)); +#endif + + updatekey(unregister, name + _T("\\ProgID"), prgid_ver); + updatekey(unregister, prgid + _T("\\CurVer"), prgid_ver); + updatekey(unregister, prgid_ver + _T("\\CLSID"), clsid); + updatekey(unregister, prgid_ver, coclass_name); + } + else + { + updatekey(unregister, name + _T("\\ProgID"), prgid); + } + + updatekey(unregister, prgid + _T("\\CLSID"), clsid); + updatekey(unregister, prgid, coclass_name); + } + + updatekey(unregister, name, coclass_name); + +// if (progid != 0) { +// updatekey(unregister, name + _T("\\ProgID"), progid); +// updatekey(unregister, tstring(progid) + _T("\\CLSID"), clsid); +// updatekey(unregister, progid, coclass_name); +// } + } + + typedef reghelper_t reghelper; + +#ifndef COMET_PARTIAL_SPECIALISATION + + template + struct entry_builder; + + template struct THE_FOLLOWING_COCLASS_HAS_NOT_BEEN_IMPLEMENTED; + template<> struct THE_FOLLOWING_COCLASS_HAS_NOT_BEEN_IMPLEMENTED {}; + + template<> struct entry_builder + { + template + struct registration + { + COMET_FORCEINLINE static void perform(const TCHAR*, bool, bool, const GUID* ) + { +#ifndef COMET_ALLOW_UNIMPLEMENTED_COCLASSES + THE_FOLLOWING_COCLASS_HAS_NOT_BEEN_IMPLEMENTED x; +#endif + } + }; + + template + struct factory + { + COMET_FORCEINLINE static ::IUnknown* get(const CLSID&) + { + return 0; + } + }; + }; // entry_builder + + template<> struct entry_builder + { + template + struct registration + { + static void perform(const TCHAR *filename, bool unregister, bool inproc_server, const GUID* appid) + { + if(unregister) { + try { + custom_registration().on_unregister(filename); + } + catch(...) + { + + } + } + + reghelper::update_coclass(unregister, + uuidof(), + filename, + tm_properties::thread_model>::string(), + CLASS::name(), + coclass_implementation::get_progid(), + CLASS::major_version, + uuidof(), + inproc_server, + appid); + + if(!unregister) + custom_registration().on_register(filename); + } // perform + }; // registration + + + template + struct factory_builder { }; + + template<> struct factory_builder + { + template + struct factory + { + typedef class_factory is_factory; + }; + }; + template<> struct factory_builder + { + template + struct factory + { + typedef class_factory_agg is_factory; + }; + }; + template<> struct factory_builder + { + template + struct factory + { + typedef class_factory_singleton is_factory; + }; + }; + + template + struct factory_type + { + typedef COMET_STRICT_TYPENAME factory_builder< CLASS::factory_type >::factory::is_factory factory; + }; + template + struct factory + { + typedef typename factory_type< coclass_implementation, LOCK_MODULE >::factory CLASS_FACTORY; + + COMET_FORCEINLINE static ::IUnknown* get(const CLSID& clsid) + { + static CLASS_FACTORY class_factory_; + + // NB: This might cause problems if CLASS_FACTORY + // implemented more than one interface. If so, this logic will + // have to be changed to inline the QueryInterface call instead. + if (clsid == uuidof()) + return &class_factory_; + return 0; + } // get + }; // factory + }; // entry_builder + + template + struct coclass_table_entry + { + // We use sizeof here to determine if there is a specialization of coclass_implementation. + // It is relying on the fact that any real implementation of coclass_implementation + // must be at least sizeof IUnknown, whereas the default sizeof implementation + // is always just a dummy class. + enum { is_undefined = sizeof (coclass_implementation) == sizeof( coclass_implementation) }; + + typedef typename entry_builder::factory factory; + typedef typename entry_builder::registration registration; + }; + +#else // COMET_PARTIAL_SPECIALISATION + template + struct entry_builder + { + typedef nil factory; + typedef nil registration; + }; + + template struct entry_builder + { + struct registration + { + COMET_FORCEINLINE static void perform(const TCHAR*, bool, bool, const GUID* ) {} + }; + + struct factory + { + COMET_FORCEINLINE static ::IUnknown* get(const CLSID&) + { + return 0; + } + }; + }; // entry_builder + + template + struct factory_builder_aux { }; + + template struct factory_builder_aux + { + typedef class_factory is_factory; + }; + template struct factory_builder_aux + { + typedef class_factory_agg is_factory; + }; + template struct factory_builder_aux + { + typedef class_factory_singleton is_factory; + }; + + + template struct entry_builder + { + struct registration + { + static void perform(const TCHAR *filename, bool unregister, bool inproc_server, const GUID* appid) + { + if(unregister) { + try { + custom_registration().on_unregister(filename); + } catch(...) {} + } + + reghelper::update_coclass(unregister, + uuidof(), + filename, + tm_properties::thread_model>::string(), + CLASS::name(), + coclass_implementation::get_progid(), + CLASS::major_version, + uuidof(), + inproc_server, + appid); + + if(!unregister) + custom_registration().on_register(filename); + } // perform + }; // registration + + struct factory_type + { + enum {type_ = coclass_implementation::factory_type }; + typedef typename factory_builder_aux< type_, coclass_implementation, FACTORY_LOCK_MODULE >::is_factory factory; + }; + struct factory + { + typedef typename factory_type::factory CLASS_FACTORY; + + COMET_FORCEINLINE static ::IUnknown* get(const CLSID& clsid) + { + static CLASS_FACTORY class_factory_; + + // NB: This might cause problems if CLASS_FACTORY + // implemented more than one interface. If so, this logic will + // have to be changed to inline the QueryInterface call instead. + if (clsid == uuidof()) + return &class_factory_; + return 0; + } // get + }; // factory + }; // entry_builder + + template + struct coclass_table_entry + { + // We use sizeof here to determine if there is a specialization of coclass_implementation. + // It is relying on the fact that any real implementation of coclass_implementation + // must be at least sizeof IUnknown, whereas the default sizeof implementation + // is always just a dummy class. + enum { is_undefined = (sizeof(coclass_implementation) == sizeof(coclass_implementation)) }; + + typedef typename entry_builder::factory factory; + typedef typename entry_builder::registration registration; + }; +#endif // COMET_PARTIAL_SPECIALISATION + + } // namespace impl + + /*! \addtogroup Server + */ + //@{ + template + class coclass_table + { + public: + COMET_FORCEINLINE static ::IUnknown* find(const CLSID& clsid) + { + ::IUnknown *ret = impl::coclass_table_entry::factory::get(clsid); + return ret ? ret : coclass_table::find(clsid); + } + + COMET_FORCEINLINE static void registration(const TCHAR* filename, bool unregister, bool inproc_server = true, const GUID* appid = 0) + { + impl::coclass_table_entry::registration::perform(filename, unregister, inproc_server, appid); + coclass_table::registration(filename, unregister, inproc_server, appid); + } + }; + + struct coclass_term + { + COMET_FORCEINLINE static ::IUnknown* find(const IID&) + { + return 0; + } + COMET_FORCEINLINE static void registration(const TCHAR*, bool, bool = true, const GUID* = 0) {} + }; + +// template<> class coclass_table > : public coclass_term {}; + template<> class coclass_table : public coclass_term {}; + template<> class coclass_table : public coclass_term {}; + + enum { NO_EMBEDDED_TLB = 1}; + + template + struct com_server_traits { + enum { flags = FL }; + enum { embedded_tlb = !(flags & NO_EMBEDDED_TLB) }; + }; + /** Main COM DLL module implementation. + * Called my the COMET_DECLARE_DLL_FUNCTIONS macro - provides implementation + * for the DLL entry points. + * \sa COMET_DECLARE_DLL_FUNCTIONS + */ + template > class com_server + { + typedef coclass_table COCLASS_TABLE; + public: + static BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID); + static HRESULT DllCanUnloadNow(); + static HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv); + static HRESULT DllRegisterServer(); + static HRESULT DllUnregisterServer(); + }; + //@} + template + BOOL WINAPI com_server::DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) + { + if (dwReason == DLL_PROCESS_ATTACH) + { + module().instance(hInstance); + DisableThreadLibraryCalls(hInstance); + + // initialize static variables in factory::get to avoid potential thread safety problem. + ::IUnknown* cf = COCLASS_TABLE::find(IID_NULL); + /* prevent warning */ cf; + } + else if (dwReason == DLL_PROCESS_DETACH) + { + module().shutdown(); + } + return TRUE; + } + + template + HRESULT com_server::DllCanUnloadNow() + { + return module().rc() == 0 ? S_OK : S_FALSE; + } + + template + HRESULT com_server::DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) + { + ::IUnknown* cf = COCLASS_TABLE::find(rclsid); + if (cf == 0) return CLASS_E_CLASSNOTAVAILABLE; + + return cf->QueryInterface(riid, ppv); + } + + namespace impl { + template + struct typelibrary_registration + { + static void convert(wchar_t *dest, const char *src) + { + size_t cs = 0; +#if _MSC_VER >=1400 + ::mbstowcs_s(&cs, dest, MAX_PATH, src, MAX_PATH); +#else + cs = ::mbstowcs(dest,src, MAX_PATH); +#endif + } + static void convert(wchar_t *dest, const wchar_t *src) + { +#if _MSC_VER >= 1400 + ::wcscpy_s(dest, MAX_PATH, src); +#else + ::wcscpy(dest, src); +#endif + } + static HRESULT perform(const TCHAR *filename, bool unregister) throw() + { + wchar_t wfilename[MAX_PATH]; + convert(wfilename, filename); + ITypeLib* tl; + HRESULT hr = LoadTypeLib(wfilename, &tl); + if (SUCCEEDED(hr)) { + if(unregister) { + TLIBATTR *tla = 0; + hr = tl->GetLibAttr(&tla); + if (SUCCEEDED(hr)) { + hr = UnRegisterTypeLib(tla->guid, tla->wMajorVerNum, tla->wMinorVerNum, tla->lcid, tla->syskind); + tl->ReleaseTLibAttr(tla); + } + } + else + hr = RegisterTypeLib(tl, wfilename, 0); + tl->Release(); + } + return hr; + } + }; + + template<> struct typelibrary_registration<0> + { + COMET_FORCEINLINE static HRESULT perform(const TCHAR *, bool) + { + return S_OK; + } + }; + } // namespace impl + + template + HRESULT com_server::DllRegisterServer() + { + TCHAR filename[MAX_PATH]; + + GetModuleFileName(module().instance(), filename, MAX_PATH); + + { + HRESULT hr = impl::typelibrary_registration::perform(filename, false); + if(FAILED(hr)) return SELFREG_E_TYPELIB; + } + + try { + COCLASS_TABLE::registration(filename, false); + } + catch (const com_error &e) + { + DllUnregisterServer(); + return impl::return_com_error(e); + } + catch (const std::exception &e) + { + DllUnregisterServer(); + ::OutputDebugStringA(e.what()); + return E_FAIL; + } + + return S_OK; + } + + template + HRESULT com_server::DllUnregisterServer() + { + TCHAR filename[MAX_PATH]; + GetModuleFileName(module().instance(), filename, MAX_PATH); + + impl::typelibrary_registration::perform(filename, true); + + COCLASS_TABLE::registration(filename, true); + return S_OK; + } +/** Declares the DLL Functions and passes them through to \a SERVER static + * functions. + * \sa com_server + */ + +#define COMET_DECLARE_DLL_FUNCTIONS(SERVER) \ +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) \ +{ \ + return SERVER::DllMain(hInstance, dwReason, 0); \ +} \ + \ +STDAPI DllCanUnloadNow() \ +{ \ + return SERVER::DllCanUnloadNow(); \ +} \ + \ +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) \ +{ \ + return SERVER::DllGetClassObject(rclsid, riid, ppv); \ +} \ + \ +STDAPI DllRegisterServer() \ +{ \ + return SERVER::DllRegisterServer(); \ +} \ + \ +STDAPI DllUnregisterServer() \ +{ \ + return SERVER::DllUnregisterServer(); \ +} + +} + +#endif diff --git a/lib/local/CamCom/comet/smart_enum.h b/lib/local/CamCom/comet/smart_enum.h new file mode 100644 index 00000000..c45f53be --- /dev/null +++ b/lib/local/CamCom/comet/smart_enum.h @@ -0,0 +1,139 @@ +/** \file + * _NewEnum style COM enumerator backed by a smart pointer to items. + */ +/* + * Copyright 2010 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_SMART_ENUM_H +#define COMET_SMART_ENUM_H + +#include + +#include +#include + +namespace comet { + + namespace impl { + + template + class smart_enum_source + { + public: + typedef typename SmartPtr::element_type::const_iterator + const_iterator; + + explicit smart_enum_source(SmartPtr source) + : source_(source), it_(begin()) {} + + const_iterator begin() + { + return source_->begin(); + } + + const_iterator end() + { + return source_->end(); + } + + const_iterator& current() + { + return it_; + } + + private: + SmartPtr source_; + const_iterator it_; + }; + + } + + /** + * Implements _NewEnum style COM object on top of smart pointer to + * a collection. + * \param Itf Enumeration Interface. + * \param C STL Style container. + * \param T Iteration Element type + * \param CONVERTER Converts container element to \p T type. (std::identity) + * \sa stl_enumeration create_enum + */ + template< + typename Itf, typename SmartPtr, typename T, + typename CONVERTER=std::identity< + COMET_STRICT_TYPENAME SmartPtr::element_type::value_type> > + class smart_enumeration : + public impl::enumeration< + Itf, T, CONVERTER, impl::smart_enum_source > + { + public: + smart_enumeration( + SmartPtr container, const CONVERTER& converter=CONVERTER()) + : enumeration( + impl::smart_enum_source(container), converter) {} + + private: + smart_enumeration(const smart_enumeration&); + smart_enumeration& operator=(const smart_enumeration&); + }; + + /** + * Smart Enumeration creation helper. + * + * Creates the enumeration with the element type specified by the + * enumerated_type_of policy. To specify the element type explicitly, use + * smart_enumeration directly. + * + * \tparam ET Enumeration Type e.g. IEnumUnknown. + * \tparam SmartPtr Smart pointer (inferred from @a container parameter). + * + * \param container Smart pointer to an STL collection + * e.g. auto_ptr. + */ + template + inline com_ptr make_smart_enumeration(SmartPtr container) + { + typedef typename enumerated_type_of::is T; + typedef std::identity< + COMET_STRICT_TYPENAME SmartPtr::element_type::value_type> + CONVERTER; + return new smart_enumeration(container); + } + + /** + * Smart Enumeration creation helper with custom converter. + * + * Creates the enumeration with the element type specified by the + * enumerated_type_of policy. To specify the element type explicitly, use + * smart_enumeration directly. + * + * \tparam ET Enumeration Type e.g. IEnumUnknown. + * \tparam SmartPtr Smart pointer (inferred from @a container parameter). + * \tparam CONVERTER Converter type (inferred from @a converter). + * + * \param container Smart pointer to an STL collection + * e.g. auto_ptr. + * \param converter Custom converter. + */ + template + inline com_ptr make_smart_enumeration( + SmartPtr container, const CONVERTER& converter) + { + typedef typename enumerated_type_of::is T; + return new smart_enumeration( + container, converter); + } +} + +#endif diff --git a/lib/local/CamCom/comet/static_assert.h b/lib/local/CamCom/comet/static_assert.h new file mode 100644 index 00000000..b0bfe449 --- /dev/null +++ b/lib/local/CamCom/comet/static_assert.h @@ -0,0 +1,54 @@ +/** \file + Provide compile-time asserts. + See www.boost.org for most recent version including documentation. + \author John Maddock + */ +/* + * Copyright 2000, 2001 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_STATIC_ASSERT_H +#define COMET_STATIC_ASSERT_H + +#include + +// (C) Copyright John Maddock 2000. +// Permission to copy, use, modify, sell and +// distribute this software is granted provided this copyright notice appears +// in all copies. This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. + +// See http://www.boost.org for most recent version including documentation. + +/* + Revision history: + 02 August 2000 + Initial version. +*/ + +namespace comet { + namespace impl { + + template struct COMPILE_TIME_ASSERTION_FAILURE; + + template <> struct COMPILE_TIME_ASSERTION_FAILURE{}; + + template struct ct_assert_test{}; + + } +} + +#define COMET_STATIC_ASSERT( B ) typedef ::comet::impl::ct_assert_test)> comet_static_assert_typedef_ + +#endif diff --git a/lib/local/CamCom/comet/stl.h b/lib/local/CamCom/comet/stl.h new file mode 100644 index 00000000..a6ac7c02 --- /dev/null +++ b/lib/local/CamCom/comet/stl.h @@ -0,0 +1,69 @@ +/** \file + Provide STL extensions. + */ +/* + * Copyright 2001 Sofus Mortensen + * Copyright 2013 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_STL_H +#define COMET_STL_H + +#include + +#include + +// add identity, select1st, and select2nd. +#if !defined(__SGI_STL_PORT) && !defined(__MINGW32__) +namespace std { + /** \internal + */ + template struct identity : public unary_function { + T operator()(const T& t) { return t; } + }; + + /** \internal + */ + template struct select1st : public unary_function { + typename T::first_type operator()(const T& t) { return t.first; } + }; + + /** \internal + */ + template struct select2nd : public unary_function { + typename T::second_type operator()(const T& t) { return t.second; } + }; +} +#else +#include +#endif + +template class delete_fun : public std::unary_function { + public: + void operator()(T& x) { delete x; } +}; + +template class delete2nd_fun : public std::unary_function { + public: + void operator()(T& x) { delete x.second; } +}; + +template class delete1st_fun : public std::unary_function { + public: + void operator()(T& x) { delete x.first; } +}; + + + +#endif diff --git a/lib/local/CamCom/comet/stl_enum.h b/lib/local/CamCom/comet/stl_enum.h new file mode 100644 index 00000000..7537b2d5 --- /dev/null +++ b/lib/local/CamCom/comet/stl_enum.h @@ -0,0 +1,188 @@ +/** \file + * Implement _NewEnum style classes and iterators. + */ +/* + * Copyright 2000 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_STL_ENUM_H +#define COMET_STL_ENUM_H + +#include + +#include +#include +#include +#include + +namespace comet { + + namespace impl { + + template + class stl_enum_source + { + public: + typedef typename Collection::const_iterator const_iterator; + + explicit stl_enum_source( + const Collection& container, com_ptr outer) + : outer_(outer), container_(container), it_(begin()) {} + + const_iterator begin() + { + return container_.begin(); + } + + const_iterator end() + { + return container_.end(); + } + + const_iterator& current() + { + return it_; + } + + private: + + // Not copy-assignable + stl_enum_source& operator=(const stl_enum_source&); + + com_ptr outer_; + const Collection& container_; + const_iterator it_; + }; + + } + + /** \class stl_enumeration_t enum.h comet/enum.h + * Implements _NewEnum style COM object. + * \param Itf Enumeration Interface. + * \param C STL Style container. + * \param T Iteration Element type (VARIANT) + * \param CONVERTER Converts container element to \p T type. (std::identity) + * \sa stl_enumeration create_enum + */ + template< + typename Itf, typename C, typename T=VARIANT, + typename CONVERTER=std::identity, + typename TH=::IUnknown> + class stl_enumeration_t : + public impl::enumeration< + Itf, T, CONVERTER, impl::stl_enum_source > + { + public: + + stl_enumeration_t( + const C& container, TH* outer=0, + const CONVERTER& converter=CONVERTER()) + : enumeration( + impl::stl_enum_source(container, outer), converter) {} + + private: + stl_enumeration_t(const stl_enumeration_t&); + stl_enumeration_t& operator=(const stl_enumeration_t&); + }; + + /** + * STL Enumeration creation helper. + * + * Creates the enumeration with the element type specified by the + * enumerated_type_of policy. To specify the element type directly, use + * stl_enumeration_t. + * + * \param ET Enumeration Type e.g. IEnumUnknown. + */ + template + struct stl_enumeration + { + + /** Auto-Create a _NewEnum enumerator from an STL container. + * No contained object. + * \param container STL Container. + */ + template + static com_ptr create(const C& container) + { + typedef typename enumerated_type_of::is T; + typedef std::identity + CONVERTER; + return new stl_enumeration_t( + container, 0); + } + + /** Auto-Create a _NewEnum enumerator from an STL container. + * \param container STL Container. + * \param th Outer or \e this pointer. + */ + template + static com_ptr create(const C& container, TH* th) + { + typedef typename enumerated_type_of::is T; + typedef std::identity + CONVERTER; + return new stl_enumeration_t( + container, th); + } + + /** Auto-Create a _NewEnum enumerator from an STL container, specifying + * a converter. + * \param container STL Container. + * \param th Outer or \e this pointer. + * \param converter Converter type (convert Container element to + * iterator interface element types). + */ + template + static com_ptr create( + const C& container, TH* th, const CONVERTER& converter) + { + typedef typename enumerated_type_of::is T; + return new stl_enumeration_t( + container, th, converter); + } + }; + + /*! Creates IEnumVARIANT enumeration of a STL container. + * \param container STL Container. + * \param th Outer or \e this pointer. + \code + com_ptr get__NewEnum() { + return create_enum( collection_, this ); + } + \endcode + * \relates stl_enumeration + */ + template + com_ptr create_enum(const C& container, TH* th = 0) + { + return stl_enumeration::create(container, th); + } + + //! Creates IEnumVARIANT enumeration of a STL container with a converter. + /*! \param container STL Container. + * \param th Outer or \e this pointer. + * \param converter Converter type (convert Container element to VARIANT) + * \sa ptr_converter ptr_converter_select1st ptr_converter_select2nd + * \relates stl_enumeration + */ + template + com_ptr create_enum(const C& container, TH* th, CONVERTER converter) + { + return stl_enumeration::create(container, th, converter); + } + +} + +#endif diff --git a/lib/local/CamCom/comet/stream.h b/lib/local/CamCom/comet/stream.h new file mode 100644 index 00000000..7d91c43a --- /dev/null +++ b/lib/local/CamCom/comet/stream.h @@ -0,0 +1,1566 @@ +/** \file + * IStream adapters. + */ +/* + * Copyright (C) 2013 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_STREAM_H +#define COMET_STREAM_H + +#include + +#include // bstr_t +#include // com_error +#include // COMET_CATCH_CLASS_INTERFACE_BOUNDARY +#include // simple_object + +#include // assert +#include +#include // numeric_limits +#include + +#include // StringCbCopyW + +namespace comet { + +namespace impl { + + static const size_t COPY_CHUNK_SIZE = 512; + + /** + * Used to clear stream failure on exiting a scope. + * + * Useful when trying an operation and converting any failure to an + * exception. In that case there is no need for the stream object to + * also retain evidence of the failure. + * + * Note, only clears `failbit`. `eofbit` is left alone as it is a + * valid consequence of operations performed by the wrapper. `badbit` is + * also left as these errors are unrecoverable and therefore the + * stream is as good as dead; a caller of the unwrapped stream needs + * to know that. + */ + template + class stream_failure_cleanser + { + public: + explicit stream_failure_cleanser(Stream& stream) + : m_stream(stream) + {} + + ~stream_failure_cleanser() throw() + { + try + { + m_stream.clear(m_stream.rdstate() & ~std::ios_base::failbit); + } + catch (const std::exception&) + {} + } + + private: + stream_failure_cleanser(const stream_failure_cleanser&); + stream_failure_cleanser& operator=(const stream_failure_cleanser&); + + Stream& m_stream; + }; + + // These do_read/write helper functions dispatch the stream-type specific code + // so that we only need one class to implement all the stream adapters + + inline void do_istream_read( + std::istream& stream, void* buffer, ULONG buffer_size_in_bytes, + ULONG& bytes_read_out) + { + typedef std::istream stream_type; + + // + // IMPORTANT: The bytes_read_out count must be correct even in the + // error case, EVEN if that error is an exception, because the caller + // can treat an error as EOF (see Read method docs). + // + // However, in the error case it is acceptable to make the read count + // smaller than the actual number of bytes read (e.g. 0) because the + // caller is losing the unread part of the stream anyway. Losing a few + // extra bytes that are already in the buffer but not declared valid + // by the byte count will not make the situation worse. + + bytes_read_out = 0U; + + stream.read( + reinterpret_cast(buffer), + buffer_size_in_bytes / sizeof(std::istream::char_type)); + + // Any failure not caused by eof is a failure. + // Badbit is always a failure because, even if we are at eof, the stream + // encountered a problem that needs reporting. + if (stream.bad() || (stream.fail() && !stream.eof())) + { + throw std::runtime_error("Reading from stream failed"); + } + else + { + if (stream.gcount() >= 0 && + // temp conversion to unsigned larger than ULONG to catch + // values larger than ULONG + static_cast(stream.gcount()) < + (std::numeric_limits::max)() / + sizeof(std::istream::char_type)) + { + bytes_read_out = static_cast( + stream.gcount() * sizeof(std::istream::char_type)); + } + + assert(stream.eof() || bytes_read_out == buffer_size_in_bytes); + } + } + + inline void do_ostream_write( + std::ostream& stream, const void* buffer, ULONG buffer_size_in_bytes, + ULONG& bytes_written_out) + { + // If there is an error we cannot get a reliable write count, even + // if we were to use stream.rdbuf()->sputn, because the failure + // might well happen during the flush operation which doesn't let + // us find out how much was flushed before failing. + + typedef std::ostream stream_type; + + bytes_written_out = 0U; + + stream.write( + reinterpret_cast(buffer), + buffer_size_in_bytes / sizeof(std::ostream::char_type)); + + try + { + stream.flush(); + } + catch (const std::exception& e) + { + throw com_error(e.what(), STG_E_MEDIUMFULL); + } + + if (stream.good()) + { + bytes_written_out = buffer_size_in_bytes; + } + else + { + throw com_error("Writing to stream failed", STG_E_MEDIUMFULL); + } + } + + /** Ensure read position updated even in case of exception */ + template + class read_position_finaliser + { + public: + read_position_finaliser( + Stream& stream, std::streampos& new_position_out) + : m_stream(stream), m_new_position_out(new_position_out) + {} + + ~read_position_finaliser() throw() + { + try + { + // we still want the read position if previous op failed + m_stream.clear(); + + std::streampos new_position = m_stream.tellg(); + if (m_stream) + { + m_new_position_out = new_position; + } + else + { + m_new_position_out = std::streampos(); + } + } + catch (...) + { + // We tried. Nothing else we can do + // + // We likely ended up here in the exception unwinding of a + // failed seek because a non-seekable stream chose to let us + // know by throwing an exception from the streambuf + // (e.g. Boost.IOStream). Non-seekable is also non-tellable so + // telgp does the same thing. + // + // We have prevent this exception propagating else we get an + // thrown while unwinding another exception causing terminate() + m_new_position_out = std::streampos(-1); + } + } + + private: + + read_position_finaliser(const read_position_finaliser&); + read_position_finaliser& operator=(const read_position_finaliser&); + + Stream& m_stream; + std::streampos& m_new_position_out; + }; + + /** Ensure write position updated even in case of exception */ + template + class write_position_finaliser + { + public: + write_position_finaliser( + Stream& stream, std::streampos& new_position_out) + : m_stream(stream), m_new_position_out(new_position_out) + {} + + ~write_position_finaliser() throw() + { + try + { + // we still want the write position if previous op failed + m_stream.clear(); + + std::streampos new_position = m_stream.tellp(); + if (m_stream) + { + m_new_position_out = new_position; + } + else + { + m_new_position_out = std::streampos(); + } + } + catch (...) + { + // We tried. Nothing else we can do + // + // We likely ended up here in the exception unwinding of a + // failed seek because a non-seekable stream chose to let us + // know by throwing an exception from the streambuf + // (e.g. Boost.IOStream). Non-seekable is also non-tellable so + // tellp does the same thing. + // + // We have prevent this exception propagating else we get an + // thrown while unwinding another exception causing terminate() + m_new_position_out = std::streampos(-1); + } + } + + private: + write_position_finaliser(const write_position_finaliser&); + write_position_finaliser& operator=(const write_position_finaliser&); + + Stream& m_stream; + std::streampos& m_new_position_out; + }; + + /** + * Copies `streampos` out-parameter to ULARGE_INTEGER out parameter + * in the face of exceptions. + * + * Using this class means you can concentrate on keeping the `streampos` + * variable updated, confident in the knowledge the the COM-interface + * ULARGE_INTEGER parameter will eventually be updated to match it, + * no matter what. + * + * Assumes that the source out parameter is guaranteed to be valid at + * all times. Valid doesn't necessarily mean correct but that it always + * contains the value we want to set the out parameter to, even if that is + * wrong (for instance failure while calculating correct position). + */ + class position_out_converter + { + public: + + position_out_converter( + std::streampos& source_out_parameter, + ULARGE_INTEGER* destination_out_parameter) + : + m_source(source_out_parameter), + m_destination(destination_out_parameter) {} + + ~position_out_converter() + { + if (m_destination) + { + try + { + // Convert to streamoff because streampos may not + // be an integer. streamoff is guaranteed to be. + std::streamoff offset_from_beginning = m_source; + if (offset_from_beginning < 0) + { + // Invalid offset, for example seeking not supported + // The error itself is dealt with right after seeking + // so this class just has to convert it to something + // reasonable for an unsigned value + m_destination->QuadPart = 0U; + } + else + { + m_destination->QuadPart = offset_from_beginning; + } + } + catch(const std::exception&) + { + // Only way this can happen is if streampos refuses to + // convert to streamoff in which case we really are screwed. + m_destination->QuadPart = 0U; + } + } + } + + private: + position_out_converter(const position_out_converter&); + position_out_converter& operator=(const position_out_converter&); + + std::streampos& m_source; + ULARGE_INTEGER* m_destination; + }; + + /** + * Increments a total counter with an increment are end of scope, + * regardless of how that scope is ended. + * + * Assumes the increment parameter is valid at all times. + */ + class byte_count_incrementer + { + public: + byte_count_incrementer( + ULARGE_INTEGER* total, ULONG& increment) + : m_total(total), m_increment(increment) {} + + ~byte_count_incrementer() + { + if (m_total) + { + m_total->QuadPart += m_increment; + } + } + + private: + + byte_count_incrementer(const byte_count_incrementer&); + byte_count_incrementer& operator=(const byte_count_incrementer&); + + ULARGE_INTEGER* m_total; + ULONG& m_increment; + }; + + template + class position_resetter + { + public: + position_resetter(StreamTraits& traits, std::streampos position) + : + m_traits(traits), m_position(position) {} + + ~position_resetter() + { + try + { + std::streampos new_position; + m_traits.do_seek( + m_position, std::ios_base::beg, new_position); + + assert(new_position == m_position); + } + catch (const std::exception&) + {} + } + + private: + position_resetter(const position_resetter&); + position_resetter& operator=(const position_resetter&); + + StreamTraits& m_traits; + std::streampos m_position; + }; + + /** + * Calculate offset of end of stream from start. + */ + template + inline std::streamoff stream_size(StreamTraits& traits) + { + position_resetter(traits, traits.do_tell()); + + std::streampos new_position; + traits.do_seek(0, std::ios_base::end, new_position); + + return new_position; + } + + template + class stream_traits; + + template + class stream_traits + { + public: + explicit stream_traits(Stream& stream) : m_stream(stream) {} + + void do_read( + void* buffer, ULONG buffer_size_in_bytes, + ULONG& bytes_read_out) + { + do_istream_read( + m_stream, buffer, buffer_size_in_bytes, bytes_read_out); + } + + void do_write( + const void* /*buffer*/, + ULONG /*buffer_size_in_bytes*/, ULONG& bytes_written_out) + { + bytes_written_out = 0U; + throw com_error( + "std::istream does not support writing", STG_E_ACCESSDENIED); + } + + void do_seek( + std::streamoff offset, std::ios_base::seekdir way, + std::streampos& new_position_out) + { + read_position_finaliser position_out_updater( + m_stream, new_position_out); + + if (!m_stream.seekg(offset, way)) + { + throw std::runtime_error("Unable to change read position"); + } + } + + std::streampos do_tell() + { + std::streampos pos = m_stream.tellg(); + if (!m_stream) + { + throw std::runtime_error("Stream position unavailable"); + } + + return pos; + } + + void do_flush() + { + throw com_error( + "std::istream does not support committing data", + STG_E_ACCESSDENIED); + } + + private: + stream_traits(const stream_traits&); + stream_traits& operator=(const stream_traits&); + + Stream& m_stream; + }; + + template + class stream_traits + { + public: + explicit stream_traits(Stream& stream) : m_stream(stream) {} + + void do_read( + void* /*buffer*/, + ULONG /*buffer_size_in_bytes*/, ULONG& bytes_read_out) + { + bytes_read_out = 0U; + throw com_error( + "std::ostream does not support reading", STG_E_ACCESSDENIED); + } + + void do_write( + const void* buffer, + ULONG buffer_size_in_bytes, ULONG& bytes_written_out) + { + do_ostream_write( + m_stream, buffer, buffer_size_in_bytes, bytes_written_out); + } + + void do_seek( + std::streamoff offset, std::ios_base::seekdir way, + std::streampos& new_position_out) + { + write_position_finaliser position_out_updater( + m_stream, new_position_out); + + if (!m_stream.seekp(offset, way)) + { + throw std::runtime_error("Unable to change write position"); + } + } + + std::streampos do_tell() + { + std::streampos pos = m_stream.tellp(); + if (!m_stream) + { + throw std::runtime_error("Stream position unavailable"); + } + + return pos; + } + + void do_flush() + { + if (!m_stream.flush()) + { + throw std::runtime_error( + "Unable to flush buffer to output sequence"); + } + } + + private: + stream_traits(const stream_traits&); + stream_traits& operator=(const stream_traits&); + + Stream& m_stream; + }; + + template + class stream_traits + { + private: + + enum last_stream_operation + { + read, + write + }; + + public: + explicit stream_traits(Stream& stream) + : m_stream(stream), m_last_op(read) {} + + void do_read( + void* buffer, ULONG buffer_size_in_bytes, ULONG& bytes_read_out) + { + bytes_read_out = 0U; + + // sync reading position with writing position, which was the last + // one used and is allowed to be different in C++ streams but + // not COM IStreams + if (m_last_op == write) + { + m_stream.seekg(m_stream.tellp()); + // We ignore errors syncing the positions as even iostreams may + // not be seekable at all + + m_last_op = read; + } + assert(m_last_op == read); + + do_istream_read( + m_stream, buffer, buffer_size_in_bytes, bytes_read_out); + } + + void do_write( + const void* buffer, ULONG buffer_size_in_bytes, + ULONG& bytes_written_out) + { + bytes_written_out = 0U; + + // sync writing position with reading position, which was the last + // one used and is allowed to be different in C++ streams but + // not COM IStreams + if (m_last_op == read) + { + m_stream.seekp(m_stream.tellg()); + // We ignore errors syncing the positions as even iostreams may + // not be seekable at all + + m_last_op = write; + } + assert(m_last_op == write); + + do_ostream_write( + m_stream, buffer, buffer_size_in_bytes, bytes_written_out); + } + + void do_seek( + std::streamoff offset, std::ios_base::seekdir way, + std::streampos& new_position_out) + { + + // Unlike with do_read/do_write, we do not ignore errors when + // trying to sync the read/write positions as, if the + // first seek succeeded, we know the stream supports + // seeking. Therefore a later error is really an error. + + if (m_last_op == read) + { + read_position_finaliser position_out_updater( + m_stream, new_position_out); + + if (!m_stream.seekg(offset, way)) + { + throw std::runtime_error("Unable to change read position"); + } + else + { + if (!m_stream.seekp(m_stream.tellg())) + { + throw std::runtime_error( + "Unable to synchronise write position"); + } + } + } + else + { + write_position_finaliser position_out_updater( + m_stream, new_position_out); + + if (!m_stream.seekp(offset, way)) + { + throw std::runtime_error("Unable to change write position"); + } + else + { + if (!m_stream.seekg(m_stream.tellp())) + { + throw std::runtime_error( + "Unable to synchronise read position"); + } + } + } + } + + std::streampos do_tell() + { + std::streampos pos; + if (m_last_op == read) + { + pos = m_stream.tellg(); + } + else + { + pos = m_stream.tellp(); + } + + if (!m_stream) + { + throw std::runtime_error("Stream position unavailable"); + } + + return pos; + } + + void do_flush() + { + if (!m_stream.flush()) + { + throw std::runtime_error( + "Unable to flush buffer to output sequence"); + } + } + + private: + stream_traits(const stream_traits&); + stream_traits& operator=(const stream_traits&); + + Stream& m_stream; + last_stream_operation m_last_op; + }; + + /** + * Wrap COM IStream interface around C++ IOStream. + * + * Unlike C++ streams which may have separate read and write positions that + * move independently, COM IStreams assume a single combined read/write head. + * Therefore this wrapper always starts the next read or write operation + * from the where the last operation finished, regardless of whether that + * operation was a call to `Read` or `Write`. + * + * @note This only applies for as long as the read/write positions are + * modified only via this wrapper. If the positions are modified by + * directly on the underlying IOStream, it is undefined whether the + * starting point for the next call to `Read`/`Write` is syncronised + * with the end of the previous operation. + * + * If operations on the inner stream results in failure (the `failbit` + * is set), this is communicated via the COM-interface return code. The + * `failbit` is cleared before the call returns. This allows further + * wrapper methods to be called without having the clear the bit directly + * on the underlying stream. Fatal errors (`badbit`) and end-of-file + * (`eofbit`) are left unchanged and remain visible in the underlying + * stream. + */ + template + class adapted_stream : public simple_object + { + private: + + typedef impl::stream_traits< + Stream, + type_traits::super_sub_class::result, + type_traits::super_sub_class::result + > stream_traits_type; + + public: + + adapted_stream(Stream& stream, const bstr_t& optional_name) + : m_stream(stream), m_traits(stream), + m_optional_name(optional_name) {} + + /** + * Fill the given buffer with data read from the wrapped C++ stream. + * + * @param [in] buffer + * Destination of bytes to be read. + * + * @param [in] buffer_size + * Size of `buffer` and, therefore, the maximum number of bytes + * this method will read. The method may read fewer than this + * number of bytes if the request cannot be fulfilled + * (e.g. end-of-file, error), in which case only the portion of + * `buffer` up to `*read_count_out` bytes from the start + * may be considered valid. + * + * @param [out] read_count_out + * Number of bytes actually read into `buffer`. In other words, + * the extent of the valid bytes in `buffer` once this method + * returns. This value is correct whether the method returns + * success or an error code. + * + * @returns + * Error code indicating the success or failure of the method. + * @retval `S_OK` + * If `buffer` was completely filled. + * @retval `S_FALSE` if EOF reached before `buffer` was filled. + * `*read_count_out` gives the number of bytes that were read. + * @retval a COM error code + * If there was a read error. + * + * Error behaviour rationale + * ------------------------- + * + * MSDN says that, if `read_count_out` < `buffer_size`, callers + * should treat both error codes and success codes as meaning + * the end-of-file was reached. However, doing so means they will be + * unable to distinguish EOF from a genuine read error. Therefore, + * we make the stronger guarantee that only `S_FALSE` means EOF + * while an error code always indicates a read error. + * To accommodate callers who follow the documentation on MSDN, + * we ensure that the read-count out-variable is correct even if the + * wrapped stream encountered an error (`failbit`, `badbit`) or + * threw an exception, because the caller may use the byte-count to + * decide which bytes of the partial read are valid. + * + * In the error case, `*read_count_out` may be less than the number + * of bytes actually read from the wrapped stream. This is for + * performance reasons: unless we read from the wrapped stream one + * byte at a time, there is no way to calculate the the exact number + * of bytes read if an exception is thrown part way through reading. + * Byte-by-byte reading is hopelessly slow for some applications, such + * as an unbuffered stream over a network, so our behaviour is a + * pragmatic compromise. We believe it is within the (vaguely) + * documented behaviour of `IStream` to treat the out-count in this way + * for error cases as the count still delimits a valid region of bytes. + */ + virtual HRESULT STDMETHODCALLTYPE Read( + void* buffer, ULONG buffer_size, ULONG* read_count_out) + { + impl::stream_failure_cleanser state_resetter(m_stream); + + if (read_count_out) + { + *read_count_out = 0U; + } + + try + { + if (!buffer) + { + throw com_error("No buffer given", STG_E_INVALIDPOINTER); + } + + // We use a dummy read count location if one is not given so + // that do_read can keep it updated correctly even if + // do_read throws an exception. + ULONG dummy_read_count = 0U; + if (!read_count_out) + { + read_count_out = &dummy_read_count; + } + + m_traits.do_read(buffer, buffer_size, *read_count_out); + + if (*read_count_out < buffer_size) + { + assert(m_stream.eof()); + + return S_FALSE; + } + else + { + assert(*read_count_out == buffer_size); + assert(m_stream.good()); + return S_OK; + } + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("Read", "adapted_stream"); + } + + /** + * Write the given data to the wrapped C++ stream's controlled sequence. + * + * The wrapped stream is flushed before this method returns to ensure + * the data is written to the controlled sequence (and any associated + * errors are detected) rather than just to the stream's buffer. + * Therefore, best performance is obtained if this method is called with + * as much data as possible for the fewest number of flushes. + * + * @param [in] data + * Bytes to write to the controlled sequence. + * + * @param [in] data_size + * Size of `data` and the number of bytes to write. + * + * @param [out] written_count_out + * Number of bytes guaranteed to be written to the controlled + * sequence. This will be `data_size` if writing succeeded. If + * an error occurred, this may be fewer than the actual number of + * bytes written to the sequence. + * + * @returns + * Error code indicating the success or failure of the method. + * @retval `S_OK` + * If `data` was completely written to the wrapped stream's + * controlled sequence. + * @retval a COM error code + * If there was a write error. + * + * Error behaviour rationale + * ------------------------- + * + * If writing succeeds, `*written_count_out` equals `data_size`. If + * writing encountered an error `*written_count_out` will be set to zero, + * even if some bytes were written out. We do this for performance + * reasons: unless we write the data one byte at a time to the wrapped + * stream _and flush the stream after each byte_, the C++ streams don't + * provide a way to count the exact number of bytes written to the + * stream's controlled sequence. Byte-by-byte writing is hopelessly + * slow for some applications, such as a stream over a network, so our + * behaviour is a pragmatic compromise. + * We believe it is within the (vaguely) documented behaviour of + * `IStream` to treat the out-count in this way for error cases. + * It may seem strange that this method even has an out-count if it + * was no intended to be meaningful in the error case (the count would + * be equal to `data_size` in the success case), however we believe + * it was only intended to be meaningful for non-blocking writes to + * asynchronous stream (`E_PENDING` would be returned) which this + * wrapper is not (see http://bit.ly/1hZGy1L). + */ + virtual HRESULT STDMETHODCALLTYPE Write( + const void* data, ULONG data_size, ULONG* written_count_out) + { + stream_failure_cleanser state_resetter(m_stream); + + if (written_count_out) + { + *written_count_out = 0U; + } + + try + { + if (!data) + { + throw com_error("Buffer not given", STG_E_INVALIDPOINTER); + } + + // We use a dummy written count out location if one is not + // given so that do_write can keep it updated correctly even if + // do_write throws an exception. + ULONG dummy_written_count = 0U; + if (!written_count_out) + { + written_count_out = &dummy_written_count; + } + + m_traits.do_write(data, data_size, *written_count_out); + + if (*written_count_out < data_size) + { + throw com_error( + "Unable to complete write operation", STG_E_MEDIUMFULL); + } + else + { + assert(*written_count_out == data_size); + return S_OK; + } + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("Write", "adapted_stream"); + } + + virtual HRESULT STDMETHODCALLTYPE Seek( + LARGE_INTEGER offset, DWORD origin, + ULARGE_INTEGER* new_position_out) + { + stream_failure_cleanser state_resetter(m_stream); + + std::streampos new_stream_position_out = std::streampos(); + impl::position_out_converter out_param_guard( + new_stream_position_out, new_position_out); + + try + { + std::ios_base::seekdir way; + if (origin == STREAM_SEEK_CUR) + { + way = std::ios_base::cur; + } + else if (origin == STREAM_SEEK_SET) + { + way = std::ios_base::beg; + } + else if (origin == STREAM_SEEK_END) + { + way = std::ios_base::end; + } + else + { + throw com_error( + "Unrecognised stream seek origin", + STG_E_INVALIDFUNCTION); + } + + if (offset.QuadPart > + (std::numeric_limits::max)()) + { + throw com_error( + "Seek offset too large", STG_E_INVALIDFUNCTION); + } + else if (offset.QuadPart < + (std::numeric_limits::min)()) + { + throw com_error( + "Seek offset too small", STG_E_INVALIDFUNCTION); + } + else + { + try + { + m_traits.do_seek( + static_cast(offset.QuadPart), + way, new_stream_position_out); + } + // Translate logic_errors (and subtypes) as they + // correspond (very roughly) to the kinds of errors + // for which IStream::Seek is documented to return + // STG_E_INVALIDFUNCTION + catch(const std::logic_error& e) + { + throw com_error(e.what(), STG_E_INVALIDFUNCTION); + } + + return S_OK; + } + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("Seek", "adapted_stream"); + } + + /** + * Expand stream to given size. + * + * The will only increase a stream's size if it is smaller than the + * given size. If the stream size is already equal or bigger, it + * remains unchanged. + * + * Not all streams support changing size. In particular, `istream`s + * do not support this method. + */ + virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER new_size) + { + try + { + stream_failure_cleanser state_resetter(m_stream); + + position_resetter resetter( + m_traits, m_traits.do_tell()); + + if (new_size.QuadPart > 0) + { + ULARGE_INTEGER new_end_position; + new_end_position.QuadPart = new_size.QuadPart - 1; + + std::streamoff new_offset; + if ((std::numeric_limits::max)() < 0) + { + assert(!"Purely negative number!"); + throw com_error( + "Seek offset too large", STG_E_INVALIDFUNCTION); + } + else if (new_end_position.QuadPart > + static_cast( + (std::numeric_limits::max)())) + { + throw com_error( + "Seek offset too large", STG_E_INVALIDFUNCTION); + } + else + { + new_offset = static_cast( + new_end_position.QuadPart); + } + + std::streamoff existing_end = stream_size(m_traits); + + if (new_offset > existing_end) + { + std::streampos new_position; + m_traits.do_seek( + new_offset, std::ios_base::beg, new_position); + + assert(std::streamoff(new_position) == new_offset); + + // Force the stream to expand by writing NUL at + // new extent + ULONG bytes_written; + m_traits.do_write("\0", 1, bytes_written); + assert(bytes_written == 1); + } + } + + return S_OK; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("SetSize", "adapted_stream"); + } + + virtual HRESULT STDMETHODCALLTYPE CopyTo( + IStream* destination, ULARGE_INTEGER amount, + ULARGE_INTEGER* bytes_read_out, ULARGE_INTEGER* bytes_written_out) + { + stream_failure_cleanser state_resetter(m_stream); + + ULARGE_INTEGER dummy_read_out; + if (!bytes_read_out) + { + bytes_read_out = &dummy_read_out; + } + bytes_read_out->QuadPart = 0U; + + ULARGE_INTEGER dummy_written_out; + if (!bytes_written_out) + { + bytes_written_out = &dummy_written_out; + } + bytes_written_out->QuadPart = 0U; + + try + { + + if (!destination) + { + throw com_error( + "Destination stream not given", STG_E_INVALIDPOINTER); + } + + std::vector buffer(COPY_CHUNK_SIZE); + + // Perform copy operation in chunks COPY_CHUNK bytes big + // The chunk must be less than the biggest ULONG in size + // because of the limits of the Read/Write API. Of course + // it will be in any case as it would be insane to use more + // memory than that, but we make sure anyway using the first + // min comparison + do { + ULONG next_chunk_size = + static_cast( + min( + (std::numeric_limits::max)(), + min( + amount.QuadPart - bytes_read_out->QuadPart, + buffer.size()))); + + ULONG read_this_round = 0U; + ULONG written_this_round = 0U; + + // These two take care of updating the total on each pass + // round the loop as well as on termination, exception or + // natural. + // + // The means the out counts are valid even in the failure + // case. MSDN says they don't have to be but, as we can, + // we might as well + impl::byte_count_incrementer read_incrementer( + bytes_read_out, read_this_round); + + impl::byte_count_incrementer write_incrementer( + bytes_written_out, written_this_round); + + m_traits.do_read( + &buffer[0], next_chunk_size, read_this_round); + HRESULT hr = destination->Write( + &buffer[0], read_this_round, &written_this_round); + if (FAILED(hr)) + { + com_error_from_interface(destination, hr); + } + + if (read_this_round < next_chunk_size) + { + // EOF + break; + } + + } while (amount.QuadPart > bytes_read_out->QuadPart); + + return S_OK; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("CopyTo", "adapted_stream"); + } + + /** + * Flush data in the buffer to the controlled output sequence. + * + * This implementation doesn't support transactions so ignores + * the commit flags. + * + * Fails if called with an istream. + */ + virtual HRESULT STDMETHODCALLTYPE Commit(DWORD /*commit_flags*/) + { + stream_failure_cleanser state_resetter(m_stream); + + try + { + m_traits.do_flush(); + return S_OK; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("Commit", "adapted_stream"); + } + + virtual HRESULT STDMETHODCALLTYPE Revert() + { + try + { + throw com_error("Transactions not supported", E_NOTIMPL); + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("Revert", "adapted_stream"); + } + + virtual HRESULT STDMETHODCALLTYPE LockRegion( + ULARGE_INTEGER /*offset*/, ULARGE_INTEGER /*extent*/, + DWORD /*lock_type*/) + { + try + { + throw com_error( + "Locking not supported", STG_E_INVALIDFUNCTION); + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("LockRegion", "adapted_stream"); + } + + virtual HRESULT STDMETHODCALLTYPE UnlockRegion( + ULARGE_INTEGER /*offset*/, ULARGE_INTEGER /*extent*/, + DWORD /*lock_type*/) + { + try + { + throw com_error( + "Locking not supported", STG_E_INVALIDFUNCTION); + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("UnlockRegion", "adapted_stream"); + } + + /** + * Get some metadata about the stream. + * + * The name returned (if requested) is the name optionally given + * in the constructor. If not given in the constructor, an + * empty string is returned. + * + * Some fields, such as the date fields, are not valid as that data + * is not available for IOStreams. + */ + virtual HRESULT STDMETHODCALLTYPE Stat( + STATSTG* attributes_out, DWORD stat_flag) + { + try + { + if (!attributes_out) + { + throw com_error("STATSTG not given", STG_E_INVALIDPOINTER); + } + + *attributes_out = STATSTG(); + + attributes_out->type = STGTY_STREAM; + + try + { + attributes_out->cbSize.QuadPart = + impl::stream_size(m_traits); + } + catch (const std::exception&) + { + // Swallow non-bad errors as many stream are not seekable and + // therefore not sizeable + if (m_stream.bad()) + throw; + } + + // Must be last as, after we detach, any failure will leak + // memory + if (!(stat_flag & STATFLAG_NONAME)) + { + // pwcsName is NOT a BSTR. It's a null-terminated OLESTR + // managed by the COM memory allocator + + size_t buffer_size = m_optional_name.size() + 1; + size_t buffer_size_in_bytes = + buffer_size * sizeof(wchar_t); + + attributes_out->pwcsName = static_cast( + ::CoTaskMemAlloc(buffer_size_in_bytes)); + if (!attributes_out->pwcsName) + { + throw com_error( + "Unable to allocate memory for stream name", + STG_E_INSUFFICIENTMEMORY); + } + + try + { + HRESULT hr = ::StringCbCopyW( + attributes_out->pwcsName, buffer_size_in_bytes, + m_optional_name.c_str()); + if (FAILED(hr)) + { + throw com_error( + "Unable to copy stream name to STATSTG", hr); + } + } + catch(...) + { + ::CoTaskMemFree(attributes_out->pwcsName); + throw; + } + } + + return S_OK; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("Stat", "adapted_stream"); + } + + /** + * Cloning not supported for IOStreams as they are not copyable. + */ + virtual HRESULT STDMETHODCALLTYPE Clone(IStream** stream_out) + { + if (stream_out) + { + *stream_out = NULL; + } + + try + { + throw com_error( + "Cloning not supported", STG_E_INVALIDFUNCTION); + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY("Clone", "adapted_stream"); + } + + private: + + Stream& m_stream; + stream_traits_type m_traits; + bstr_t m_optional_name; + }; + + template + class adapted_stream_pointer : public simple_object + { + public: + + adapted_stream_pointer(StreamPtr stream, const bstr_t& optional_name) + : m_stream(stream), m_inner(adapt_stream(*m_stream, optional_name)) + {} + + // The forwarded methods must return their HRESULT by throwing and + // catching in order to propagate the IErrorInfo upwards. + + virtual HRESULT STDMETHODCALLTYPE Read( + void* buffer, ULONG buffer_size, ULONG* read_count_out) + { + if (read_count_out) + { + *read_count_out = 0U; + } + + try + { + HRESULT hr = m_inner->Read(buffer, buffer_size, read_count_out); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "Read", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE Write( + const void* buffer, ULONG buffer_size, ULONG* written_count_out) + { + if (written_count_out) + { + *written_count_out = 0U; + } + + try + { + HRESULT hr = m_inner->Write( + buffer, buffer_size, written_count_out); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "Write", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE Seek( + LARGE_INTEGER offset, DWORD origin, + ULARGE_INTEGER* new_position_out) + { + if (new_position_out) + { + new_position_out->QuadPart = 0U; + } + + try + { + HRESULT hr = m_inner->Seek(offset, origin, new_position_out); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "Seek", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER new_size) + { + try + { + HRESULT hr = m_inner->SetSize(new_size); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "SetSize", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE CopyTo( + IStream* destination, ULARGE_INTEGER amount, + ULARGE_INTEGER* bytes_read_out, ULARGE_INTEGER* bytes_written_out) + { + if (bytes_read_out) + { + bytes_read_out->QuadPart = 0U; + } + + if (bytes_written_out) + { + bytes_written_out->QuadPart = 0U; + } + + try + { + HRESULT hr = m_inner->CopyTo( + destination, amount, bytes_read_out, bytes_written_out); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "CopyTo", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE Commit(DWORD commit_flags) + { + try + { + HRESULT hr = m_inner->Commit(commit_flags); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "Commit", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE Revert() + { + try + { + HRESULT hr = m_inner->Revert(); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "Revert", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE LockRegion( + ULARGE_INTEGER offset, ULARGE_INTEGER extent, DWORD lock_type) + { + try + { + HRESULT hr = m_inner->LockRegion(offset, extent, lock_type); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "LockRegion", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE UnlockRegion( + ULARGE_INTEGER offset, ULARGE_INTEGER extent, DWORD lock_type) + { + try + { + HRESULT hr = m_inner->UnlockRegion(offset, extent, lock_type); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "UnlockRegion", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE Stat( + STATSTG* attributes_out, DWORD stat_flag) + { + if (attributes_out) + { + *attributes_out = STATSTG(); + } + + try + { + HRESULT hr = m_inner->Stat(attributes_out, stat_flag); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "Stat", "adapted_stream_pointer"); + } + + virtual HRESULT STDMETHODCALLTYPE Clone(IStream** stream_out) + { + if (stream_out) + { + *stream_out = NULL; + } + + try + { + HRESULT hr = m_inner->Clone(stream_out); + if (FAILED(hr)) + throw com_error_from_interface(m_inner, hr); + + return hr; + } + COMET_CATCH_CLASS_INTERFACE_BOUNDARY( + "Clone", "adapted_stream_pointer"); + } + + private: + + StreamPtr m_stream; + com_ptr m_inner; + }; + +} + +/** + * Wrap COM IStream interface around C++ IOStream. + * + * The caller must ensure that the C++ IOStream remains valid until the + * last reference to the returned wrapper is released. + * + * Unlike C++ streams which may have separate read and write positions that + * move independently, COM IStreams assume a single combined read/write head. + * Therefore this wrapper always starts the next read or write operation + * from the where the last operation finished, regardless of whether that + * operation was a call to `Read` or `Write`. + * + * @note This only applies for as long as the read/write positions are + * modified only via this wrapper. If the positions are modified by + * directly on the underlying IOStream, it is undefined whether the + * starting point for the next call to `Read`/`Write` is syncronised + * with the end of the previous operation. + * + * If operations on the inner stream results in failure (the `failbit` + * is set), this is communicated via the COM-interface return code. The + * `failbit` is cleared before the call returns. This allows further + * wrapper methods to be called without having the clear the bit directly + * on the underlying stream. Fatal errors (`badbit`) and end-of-file + * (`eofbit`) are left unchanged and remain visible in the underlying + * stream. + */ +template +inline com_ptr adapt_stream( + Stream& stream, const bstr_t& optional_name=bstr_t()) +{ + return new impl::adapted_stream(stream, optional_name); +} + +/** + * Wrap COM IStream interface around pointer (usually smart) to C++ IOStream. + * + * If the pointer type is a smart pointer, the caller need not must ensure + * the lifetime of the C++ IOStream exceeds that of the adapter; the smart + * pointer takes care of ensuring this. + * + * The main reason for having this function in addition to `adapt_stream` is + * support the common case where a COM stream is intended to be the sole owner + * of the C++ stream with which it is created. This allows the caller to + * create the IStream and forget about the C++ stream. Using `adapt_stream` + * they would have to manage the lifetime of both. + * + * When we support movable types (C++11 or Boost.Move emulation), this method + * would become unnecessary for this purpose as the C++ stream could simply + * be moved into the adapter + */ +template +inline com_ptr adapt_stream_pointer( + StreamPtr stream_pointer, const bstr_t& optional_name=bstr_t()) +{ + return new impl::adapted_stream_pointer( + stream_pointer, optional_name); +} + +} + +#endif diff --git a/lib/local/CamCom/comet/threading.h b/lib/local/CamCom/comet/threading.h new file mode 100644 index 00000000..a4a79ab4 --- /dev/null +++ b/lib/local/CamCom/comet/threading.h @@ -0,0 +1,228 @@ +/** \file + * Threading support. + */ +/* + * Copyright 2001-2002 Sofus Mortensen, Mikael Lindgren + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_THREADING_H +#define COMET_THREADING_H + +#include +#include + +#include + +namespace comet { + +/** \class critical_section threading.h comet/threading.h + * wrapper for Win32 CRITICAL_SECTION. + */ +class critical_section { +public: + critical_section() { + ::InitializeCriticalSection(&cs_); + } + + ~critical_section() { + ::DeleteCriticalSection(&cs_); + } + + void enter() const { + ::EnterCriticalSection(&cs_); + } + + void leave() const { + ::LeaveCriticalSection(&cs_); + } + +private: + critical_section(const critical_section&); + critical_section& operator=(const critical_section&); + + mutable CRITICAL_SECTION cs_; +}; + +/** \class auto_cs threading.h comet/threading.h + * Stack based critical-section resource obtaining and releasing. + */ +class auto_cs { +public: + explicit auto_cs(const critical_section& cs) : cs_(cs) { + cs_.enter(); + } + + ~auto_cs() { + cs_.leave(); + } + +private: + auto_cs& operator=(const auto_cs&); + auto_cs(const auto_cs&); + + const critical_section& cs_; +}; + +/** \class locking_ptr threading.h comet/threading.h + Locking pointer. + Based on an article on the volatile keyword by Andrei Alexandrescu + See article. + */ +template class locking_ptr { +public: + locking_ptr(volatile T& obj, critical_section& cs) : + pointer_(const_cast(&obj)), + cs_(cs) + { + cs_.enter(); + } + + ~locking_ptr() + { + cs_.leave(); + } + + T& operator*() + { + return *pointer_; + } + + T* operator->() + { + return pointer_; + } + +private: + T* pointer_; + critical_section& cs_; + + locking_ptr(const locking_ptr&); + locking_ptr& operator=(const locking_ptr&); +}; + +class thread +{ +private: + DWORD id_; + auto_handle handle_; + + // declare non-copyable + thread(thread const& ); + thread& operator=(thread const& ); +public: + thread() : id_(0) + {} + + const auto_handle& start() + { + handle_ = auto_attach(::CreateThread(0, 0, &thread::start_proc, this, 0, &id_)); + return handle_; + } + + const auto_handle& handle() const + { + return handle_; + } + + void exit(DWORD exit_code) + { + ::ExitThread(exit_code); + } + + void suspend() + { + ::SuspendThread(handle_); + } + + void resume() + { + ::ResumeThread(handle_); + } + + bool running() + { + DWORD code; + return handle_ && + ::GetExitCodeThread(handle_, &code) && + code == STILL_ACTIVE; + } + + bool wait(DWORD timeout = INFINITE) + { + return ::WaitForSingleObject(handle_, timeout) == WAIT_OBJECT_0; + } + +private: + static DWORD _stdcall start_proc(void* param) + { + return static_cast(param)->thread_main(); + } + + virtual DWORD thread_main() = 0; +}; + +class event +{ +private: + auto_handle handle_; + + // declare non-copyable + event(event const& ); + event& operator=(event const& ); +public: + explicit event(bool initial_state = false) + { + handle_ = auto_attach(::CreateEvent(0, false, initial_state, 0)); + } + + explicit event(const TCHAR* name, bool initial_state = false) + { + handle_ = auto_attach(::CreateEvent(0, false, initial_state, name)); + } + + void set() + { + if (0 == ::SetEvent(handle_)) + { + DWORD err = GetLastError(); + raise_exception(HRESULT_FROM_WIN32(err)); + } + } + + void reset() + { + if (0==::ResetEvent(handle_) ) + { + DWORD err = GetLastError(); + raise_exception(HRESULT_FROM_WIN32(err)); + } + } + + bool wait(DWORD timeout = INFINITE) + { + return ::WaitForSingleObject(handle_, timeout) == WAIT_OBJECT_0; + } + + bool is_set() + { + return ::WaitForSingleObject(handle_, 0) == WAIT_OBJECT_0; + } + + operator HANDLE() const + { return handle_; } +}; + +} + +#endif diff --git a/lib/local/CamCom/comet/tlbinfo.h b/lib/local/CamCom/comet/tlbinfo.h new file mode 100644 index 00000000..38a4900e --- /dev/null +++ b/lib/local/CamCom/comet/tlbinfo.h @@ -0,0 +1,451 @@ +/** \file + * Wrappers for ITypeLibInfo. + */ +/* + * Copyright 2002 Michael Geddes. + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_TLIBINFO_H +#define COMET_TLIBINFO_H + +#ifdef _SHOW_INC +#pragma message(" #Include " __FILE__) +#endif + +#include +#include +#include + +namespace comet +{ + template<> struct comtype + { + static const IID& uuid() throw() { return IID_IPictureDisp; } + typedef ::IUnknown base; + }; + template<> struct comtype + { + static const IID& uuid() throw() { return IID_ITypeInfo; } + typedef ::IUnknown base; + }; + template<> struct comtype + { + static const IID& uuid() throw() { return IID_ITypeInfo2; } + typedef ::ITypeInfo base; + }; + /*! \addtogroup Interfaces + */ + //@{ + namespace impl + { + /// A class to handle the auto-releaseing of structs returned by by the Typeinfo interfaces. + template< typename B, typename T> + struct tlib_info + { + typedef void (__stdcall B::*RelFunc)(T*); + template< RelFunc RELEASE> + struct typeinfo_attr_base + { + public: + typeinfo_attr_base() : info_(NULL), attrib_(NULL), rc_(NULL) {} + typeinfo_attr_base( const com_ptr &info, T *attrib ) : info_(info),attrib_(attrib),rc_(NULL) + { + if (attrib != NULL) + { + rc_ = new long; + (*rc_)=1; + } + } + typeinfo_attr_base( const typeinfo_attr_base &base) + : info_(base.info_), attrib_(base.attrib_), rc_(base.rc_) + { + ++*rc_; + } + ~typeinfo_attr_base() + { + release(); + } + typeinfo_attr_base &operator =( const typeinfo_attr_base &base) + { + release(); + info_= base.info_; + attrib_= base.attrib_; + rc_ = base.rc_; + ++*rc_; + return *this; + } + void release() + { + if(attrib_ != NULL && rc_ != NULL ) + { + if (0 == --*rc_) + { + (info_.raw()->*RELEASE)( attrib_); + delete rc_; + } + rc_ = NULL; + attrib_ = NULL; + info_ = NULL; + } + } + + + T *operator->() const + { + if( attrib_ ==NULL ) throw com_error(E_POINTER); + return attrib_; + } + bool is_null() const { return attrib_ == NULL; } + protected: + long *rc_; + T *attrib_; + com_ptr info_; + }; + }; + }; + + /// Auto-release wrapper for TYPEATTR. + typedef impl::tlib_info::typeinfo_attr_base< &ITypeInfo::ReleaseTypeAttr > type_attr_t; + /// Auto-release wrapper for FUNCDESC. + typedef impl::tlib_info::typeinfo_attr_base< &ITypeInfo::ReleaseFuncDesc > func_desc_t; + /// Auto-release wrapper for VARDESC. + typedef impl::tlib_info::typeinfo_attr_base< &ITypeInfo::ReleaseVarDesc > var_desc_t; + + /// Auto-release wrapper for TLIBATTR. + typedef impl::tlib_info::typeinfo_attr_base< &ITypeLib::ReleaseTLibAttr > tlibattr_t; + + + /** Flags for GetImplTypeFlags. + * \sa IMPLTYPEFLAGS + */ + enum impl_type_flag + { + eft_default=0x1, ///< The interface or dispinterface represents the default for the source or sink. + eft_source =0x2, ///< This member of a coclass is called rather than implemented. + eft_restricted=0x4, ///< The member should not be displayed or programmable by users. + eft_defaultvtable=0x8 ///< Sinks receive events through the VTBL. + }; + + /** Specialisation wrapper for ITypeInfo. + * \struct wrap_t tlbinfo.h comet/tlbinfo.h + * Wrapper for ITypeInfo typelib information. + * \sa ITypeInfo + */ + template<> + struct wrap_t + { + private: + inline ITypeInfo *raw_(){ return reinterpret_cast(this); } + public: + + /** Returns a wrapped Type Attributes struct. + * \sa TYPEATTR + */ + type_attr_t GetTypeAttr() + { + TYPEATTR *pTypeAttr; + raw_()->GetTypeAttr( &pTypeAttr) | raise_exception ; + return type_attr_t( raw_(), pTypeAttr); + } + + /// Returns an ITypeComp interface. + com_ptr GetTypeComp() + { + com_ptr ppTComp; + raw_()->GetTypeComp( ppTComp.out()) | raise_exception; + return ppTComp; + } + + /** Get the function description struct. + * \sa FUNCDESC + */ + func_desc_t GetFuncDesc( unsigned int index) + { + FUNCDESC *pFuncDesc ; + raw_()->GetFuncDesc(index, &pFuncDesc) | raise_exception ; + return func_desc_t( raw_(), pFuncDesc); + } + + /** Get the Variable/Constant/Datamember description. + * \sa VARDESC + */ + var_desc_t GetVarDesc( unsigned int index ) + { + VARDESC *pVarDesc; + raw_()->GetVarDesc( index,&pVarDesc) | raise_exception; + return var_desc_t(raw_(), pVarDesc); + } + + +#ifdef NOT_YET + std::list GetNames( MEMBERID memid ) + { + /* [local] */ HRESULT GetNames( /* [in] */ MEMBERID memid, /* [length_is][size_is][out] */ BSTR *rgBstrNames, + /* [in] */ UINT cMaxNames, /* [out] */ UINT *pcNames) ; + } +#endif // + + /// Retrieves the type description of implemented interface types. + HREFTYPE GetRefTypeOfImplType( unsigned int index) + { + HREFTYPE reftype; + raw_()->GetRefTypeOfImplType(index, &reftype) | raise_exception; + return reftype; + } + + /// Retrieves the type description of implemented interface types. + long GetImplTypeFlags( int index) + { + INT implTypeFlags; + raw_()->GetImplTypeFlags( index, &implTypeFlags) |raise_exception; + return implTypeFlags; + } + +#ifdef NOT_YET + std::vector GetIDsOfNames( const std::vector &rgsNames); + { + /* [local] */ HRESULT GetIDsOfNames( + /* [size_is][in] */ LPOLESTR *rgszNames, + /* [in] */ UINT cNames, + /* [size_is][out] */ MEMBERID *pMemId) ; + } + + /* [local] */ HRESULT Invoke( + /* [in] */ PVOID pvInstance, + /* [in] */ MEMBERID memid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS *pDispParams, + /* [out] */ VARIANT *pVarResult, + /* [out] */ EXCEPINFO *pExcepInfo, + /* [out] */ UINT *puArgErr) ; +#endif // NOT_YET + /// Raw wrapper to make sure comet still works/compiles. + HRESULT GetIDsOfNames( LPOLESTR *rgszNames, UINT cNames, + MEMBERID *pMemId) + { + return raw_()->GetIDsOfNames( rgszNames, cNames, pMemId); + } + + /// Raw wrapper to make sure comet still works/compiles. + HRESULT Invoke( PVOID pvInstance, MEMBERID memid, WORD wFlags, + DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, + UINT *puArgErr) + { + return raw_()->Invoke( pvInstance, memid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + } + + bool GetIDOfName( const wchar_t *name, MEMBERID *id) + { + wchar_t *ucname = const_cast(name); + return SUCCEEDED(raw_()->GetIDsOfNames( &ucname, 1, id)); + } + + + /// Get the documentation for the specified MEMBERID. + void GetDocumentation(MEMBERID memid, bstr_t *name, bstr_t *docString, DWORD *helpcontext, bstr_t *helpfile) + { + raw_()->GetDocumentation(memid, name?name->out():NULL, docString?docString->out():NULL, + helpcontext, helpfile? helpfile->out():NULL) | raise_exception; + } + /** Get the member name for the specified MEMBERID. + * This is a shortcut for GetDocumentation. + */ + + bstr_t GetMemberName(MEMBERID memid) + { + bstr_t name; + raw_()->GetDocumentation(memid, name.out(), NULL, NULL, NULL) | raise_exception; + return name; + } + bstr_t GetName() + { + return GetMemberName(-1); + } + +#if NOT_YET + /* [local] */ HRESULT GetDllEntry( + /* [in] */ MEMBERID memid, + /* [in] */ INVOKEKIND invKind, + /* [out] */ BSTR *pBstrDllName, + /* [out] */ BSTR *pBstrName, + /* [out] */ WORD *pwOrdinal) ; +#endif // NOT_YET + + /** Get the referenced Type information. + */ + com_ptr GetRefTypeInfo( HREFTYPE refType) + { + com_ptr refinfo; + raw_()->GetRefTypeInfo( refType, refinfo.out()) | raise_exception; + return refinfo; + } + +#if NOT_YET + /* [local] */ HRESULT AddressOfMember( /* [in] */ MEMBERID memid, /* [in] */ INVOKEKIND invKind, + /* [out] */ PVOID *ppv) ; + + /* [local] */ HRESULT CreateInstance( /* [in] */ IUnknown *pUnkOuter, /* [in] */ REFIID riid, + /* [iid_is][out] */ PVOID *ppvObj) ; + + HRESULT GetMops( /* [in] */ MEMBERID memid, /* [out] */ BSTR *pBstrMops) ; +#endif // NOT_YET + + /** Get the typelib containing this definition. + * \return A pair containing the typelibrary and the index of the type description within the type library. + */ + std::pair< com_ptr, UINT > GetContainingTypeLib() + { + std::pair< com_ptr, UINT> result; + raw_()->GetContainingTypeLib( result.first.out(), &(result.second) ) | raise_exception; + return result; + } + }; + //@} + + template<> + struct wrap_t : wrap_t + { + TYPEKIND GetTypeKind() + { + TYPEKIND tkind; + raw_()->GetTypeKind(&tkind) | raise_exception; + return tkind; + } + private: + inline ITypeInfo2 *raw_(){ return reinterpret_cast(this); } + }; + + + /*! \addtogroup Interfaces + */ + //@{ + + /// Specialisation to handle TypeLibrary API. + template<> + struct wrap_t + { + inline ITypeLib *raw_(){ return reinterpret_cast(this); } + unsigned int GetTypeInfoCount() + { + return raw_()->GetTypeInfoCount(); + } + + /// Get typeinfo at specified index. + com_ptr GetTypeInfo( UINT index) + { + com_ptr tinfo; + raw_()->GetTypeInfo( index, tinfo.out()) | raise_exception; + return tinfo; + } + + /// Get type of information at specified index. + TYPEKIND GetTypeInfoType( UINT index) + { + TYPEKIND retval; + raw_()->GetTypeInfoType( index, &retval) | raise_exception; + return retval; + } + + /// Get typeinfo given GUID. + com_ptr GetTypeInfoOfGuid( const uuid_t &guid ) + { + com_ptr tinfo; + raw_()->GetTypeInfoOfGuid( guid, tinfo.out()) | raise_exception; + return tinfo; + } + /// Get Raw Typeinfo of guid. + HRESULT GetTypeInfoOfGuid( REFGUID guid, ITypeInfo **ppTinfo) + { + return raw_()->GetTypeInfoOfGuid( guid,ppTinfo); + } + + /// Get attributes of the typelibrary. + tlibattr_t GetLibAttr() + { + TLIBATTR *attr; + raw_()->GetLibAttr( &attr) | raise_exception; + return tlibattr_t( raw_(), attr); + } + + com_ptr GetTypeComp() + { + com_ptr typecomp; + raw_()->GetTypeComp( typecomp.out() ) | raise_exception; + return typecomp; + } + + void GetDocumentation(int index, bstr_t *name, bstr_t *docString, DWORD *helpcontext, bstr_t *helpfile) + { + raw_()->GetDocumentation(index, name?name->out():NULL, docString?docString->out():NULL, + helpcontext, helpfile? helpfile->out():NULL) | raise_exception; + } + bstr_t GetItemName(int index) + { + bstr_t name; + raw_()->GetDocumentation(index, name.out(), NULL, NULL, NULL) | raise_exception; + return name; + } + bstr_t GetName() + { + return GetItemName(-1); + } + + bool IsName( const bstr_t &name, unsigned long hashval ) + { + BOOL ret; + raw_()->IsName( name.in(), hashval, &ret) | raise_exception; + return ret!=0; + } + + std::pair, MEMBERID> FindName( const bstr_t &name) + { + std::pair, MEMBERID> result; + USHORT tofind = 1; + raw_()->FindName(name.in(), 0, result.first.out(), &result.second, &tofind) | raise_exception; + return result; + } +#ifdef NOT_YET + std::list, MEMBERID> >FindName( const bstr_t &name, int max) + {} + [local] + HRESULT FindName( + [in, out] LPOLESTR szNameBuf, + [in] ULONG lHashVal, + [out,size_is(*pcFound),length_is(*pcFound)] ITypeInfo **ppTInfo, + [out,size_is(*pcFound),length_is(*pcFound)] MEMBERID * rgMemId, + [in, out] USHORT * pcFound + ); +#endif // LATER + + + }; + + /// Load typeinfo from a file. + namespace typeinfo + { + /** Whether to register the typelibrary on load. + * \relates typeinfo::LoadTypeLib + */ + enum regkind_t { reg_default = REGKIND_DEFAULT, reg_register = REGKIND_REGISTER, reg_none = REGKIND_NONE } ; + /// Load typeinfo from a file. + static inline com_ptr LoadTypeLib( const bstr_t &filename, regkind_t regkind= reg_default) + { + com_ptr tlib; + LoadTypeLibEx(filename.in(), (REGKIND)(regkind), tlib.out()) | raise_exception; + return tlib; + } + } + //@} +} // namespace comet +#endif /* COMET_TLIBINFO_H */ diff --git a/lib/local/CamCom/comet/tstring.h b/lib/local/CamCom/comet/tstring.h new file mode 100644 index 00000000..3b54677b --- /dev/null +++ b/lib/local/CamCom/comet/tstring.h @@ -0,0 +1,125 @@ +/** \file + * C++ Std. lib TCHAR mappings. + */ +/* + * Copyright 2002 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_TSTRING_H +#define COMET_TSTRING_H + +#include +#include +#include +#include + +namespace comet { + + /*! \addtogroup WinUtil + */ + //@{ + +#if defined(_MBCS) && !defined(_UNICODE) && !defined(COMET_NO_MBCS_WARNING) +#pragma message( "Warning: _MBCS is defined. Be aware that tstring does not support multi-byte character strings" ) +#endif + /** \page comettstring Comet tstring type. + * In order for comet projects to more easily support unicode via the + * windows file "tchar.h", many comet classes support the type #comet::tstring which is + * a convenience typedef to basic_string. Associated with + * \link comet::tstring tstring \endlink are all or most of the other STL + * classes that have a \p char argument to the templates. + * + * In addition to being used by comet, this is a generally useful series + * of typedefs to be used when combining STL types with tchar.h compatible + * projects. + * + * There are two area to be particularly wary of with STL and tchar.h. + * + * The first is that the filename argument for file streams are always + * narrow char *. I would suggest using a #comet::bstr_t which will + * convert for you, especially in UNICODE (actually UCS2 little-endien) projects. + * + * The second is that tstring support for multi-byte character strings is + * very minimal. Embedded NULLs in the stream are not a problem for + * copying around, however none of the parsing/searching/comparison + * methods cope with multi-byte character sets. + * + * I believe that part of the reason for this is the platform specific + * nature of multi-byte, and the internal support for a variety of + * different MBCS implementations by Microsoft. + */ + + /** TCHAR version of std::basic_string. + * See \ref comettstring + */ + typedef std::basic_string< TCHAR > tstring; + + /** TCHAR version of std::basic_ios. + * \relates tstring + */ + typedef std::basic_ios > tios; + /** TCHAR version of std::basic_streambuf. + * \relates tstring + */ + typedef std::basic_streambuf > tstreambuf; + /** TCHAR version of std::basic_istream. + * \relates tstring + */ + typedef std::basic_istream > tistream; + /** TCHAR version of std::basic_ostream. + * \relates tstring + */ + typedef std::basic_ostream > tostream; + /** TCHAR version of std::basic_iostream. + * \relates tstring + */ + typedef std::basic_iostream > tiostream; + /** TCHAR version of std::basic_stringbuf. + * \relates tstring + */ + typedef std::basic_stringbuf, std::allocator > tstringbuf; + /** TCHAR version of std::basic_istringstream. + * \relates tstring + */ + typedef std::basic_istringstream, std::allocator > tistringstream; + /** TCHAR version of std::basic_ostringstream. + * \relates tstring + */ + typedef std::basic_ostringstream, std::allocator > tostringstream; + /** TCHAR version of std::basic_stringstream. + * \relates tstring + */ + typedef std::basic_stringstream, std::allocator > tstringstream; + /** TCHAR version of std::basic_filebuf. + * \relates tstring + */ + typedef std::basic_filebuf > tfilebuf; + /** TCHAR version of std::basic_ifstream. + * \relates tstring + */ + typedef std::basic_ifstream > tifstream; + /** TCHAR version of std::basic_ofstream. + * \relates tstring + */ + typedef std::basic_ofstream > tofstream; + /** TCHAR version of std::basic_fstream. + * \relates tstring + */ + typedef std::basic_fstream > tfstream; + + //@} +} + + +#endif diff --git a/lib/local/CamCom/comet/type_traits.h b/lib/local/CamCom/comet/type_traits.h new file mode 100644 index 00000000..45f7c3ab --- /dev/null +++ b/lib/local/CamCom/comet/type_traits.h @@ -0,0 +1,262 @@ +/** \file + * Interrogate traits of template types. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen, Paul Hollingsworth + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +/* + * Partial copyright for is_const, is_volatile and is_reference. + * + * (C) Copyright Steve Cleary, Beman Dawes, Howard Hinnant & John Maddock 2000. + * Permission to copy, use, modify, sell and + * distribute this software is granted provided this copyright notice appears + * in all copies. This software is provided "as is" without express or implied + * warranty, and with no claim as to its suitability for any purpose. + */ + +#ifndef COMET_TYPE_TRAITS_H +#define COMET_TYPE_TRAITS_H + +namespace comet { + + /** Provides structs to interrogate traits of template types. + */ + namespace type_traits { + + ////////////////////////////////////////////////////////////////////////// + // From www.boost.org + + //* is a type T declared const - is_const + namespace detail{ + typedef char yes_result; + typedef char (&no_result)[8]; + yes_result is_const_helper(const volatile void*); + no_result is_const_helper(volatile void *); + yes_result is_volatile_helper(const volatile void*); + no_result is_volatile_helper(const void *); + + template struct helper { T t; }; + } + + template struct is_const + { + enum{ result = (sizeof(detail::yes_result) == sizeof( detail::is_const_helper( + & ((reinterpret_cast *>(0))->t)) + )) + }; + }; + + template struct is_volatile + { + enum{ result = (sizeof(detail::yes_result) == sizeof(detail::is_volatile_helper( + & ((reinterpret_cast *>(0))->t)) + )) + }; + }; + +# pragma warning(push) +# pragma warning(disable: 4181) + + //* is a type T a reference type - is_reference + template struct is_reference + { + private: + typedef T const volatile cv_t; + public: + enum // dwa 10/27/00 - VC6.4 seems to choke on short-circuit (&&,||) + { // evaluations in constant expressions + value = !is_const::result | !is_volatile::result + }; + }; + + template <> struct is_reference + { + enum{ value = false }; + }; + +# pragma warning(pop) + + + ////////////////////////////////////////////////////////////////////////// + + template struct is_integer { enum { result = false }; }; + template<> struct is_integer { enum { result = true }; }; + template<> struct is_integer { enum { result = true }; }; + template<> struct is_integer { enum { result = true }; }; + template<> struct is_integer { enum { result = true }; }; +// template<> struct is_integer { enum { result = true }; }; + template<> struct is_integer { enum { result = true }; }; + template<> struct is_integer { enum { result = true }; }; + template<> struct is_integer { enum { result = true }; }; + template<> struct is_integer { enum { result = true }; }; + template<> struct is_integer { enum { result = true }; }; + template<> struct is_integer { enum { result = true }; }; + + //! @if Internal + /** Taken from STLPort. + * \internal + */ + struct _PointerShim { + // Since the compiler only allows at most one non-trivial + // implicit conversion we can make use of a shim class to + // be sure that IsPtr below doesn't accept classes with + // implicit pointer conversion operators + _PointerShim(const volatile void*); // no implementation + }; + //! @endif + + // These are the discriminating functions + static char __cdecl _IsP(bool, _PointerShim); // no implementation is required + static char* __cdecl _IsP(bool, ...); // no implementation is required + + template + struct is_pointer { + // This template meta function takes a type T + // and returns true exactly when T is a pointer. + // One can imagine meta-functions discriminating on + // other criteria. + static _Tp& __null_rep(); + enum { result = (sizeof(_IsP(false,__null_rep())) == sizeof(char)) }; + }; + + // Work out whether the Type is something we can use operator-> on + // (hopefully). If we fail, the worst we get is a level 3 warinng. + template + struct is_class_pointer + { + enum { result = (is_pointer<_Tp>::result && ! is_integer<_Tp>::result) }; + }; + + template struct int_holder + { + enum { result = x }; + }; + + template struct type_holder + { + typedef T result; + }; + +/* template class is_static_compatible + { + class yes { }; + class no {char a[10]; }; + + static yes test( U * ); + static no test( ... ); + + public: + enum { is = sizeof(test(static_cast(0))) == sizeof(yes) ? true : false }; + };*/ + + template class is_cast_operator_compatible + { + protected: + class yes { }; + class no {char a[10]; }; + + static yes test( U* ); + static no test( ... ); + + public: + enum { is = sizeof(test(*static_cast(0))) == sizeof(yes) /*? 1 : 0 */}; + }; + +#ifndef COMET_PARTIAL_SPECIALISATION + template struct conversion_aux + { + template class X + { + protected: + class yes { }; + class no {char a[10]; }; + static T makeT(); + + static yes test(U); + static no test(...); + public: + enum { exists = sizeof(test(makeT())) == sizeof(yes) }; + enum { same_type = false }; + }; + + + template<> class X + { + public: + enum { exists = true }; + enum { same_type = true }; + }; + + }; + template struct conversion + { + enum { exists = conversion_aux::X::exists }; + enum { same_type = conversion_aux::X::same_type }; + enum { exists_two_way = exists && conversion_aux::X::exists }; + }; +#else + template struct conversion + { + protected: + class yes { }; + class no {char a[10]; }; + static T makeT(); + + static yes test(U); + static no test(...); + public: + enum { exists = sizeof(test(makeT())) == sizeof(yes) }; + enum { same_type = false }; + enum { exists_two_way = conversion::exists}; + + }; + template struct conversion + { + + enum { exists = true }; + enum { same_type = true }; + enum { exists_two_way = true}; + }; + +#endif // COMET_PARTIAL_SPECIALISATION + + template struct super_sub_class + { + enum { result = conversion::exists && !conversion::same_type }; + }; + + inline bool is_null_vtable_entry(void *p,short n) + { + return ((n<0)?true:(0==(*reinterpret_cast(reinterpret_cast(*(reinterpret_cast(p)))+n)))); + } + + } + + namespace impl { + + // used by NutshellImpl's + struct false_type {}; + struct true_type {}; + + template struct is_one { typedef false_type val; }; + template<> struct is_one<1> { typedef true_type val; }; + + } + + +} + + +#endif diff --git a/lib/local/CamCom/comet/typelist.h b/lib/local/CamCom/comet/typelist.h new file mode 100644 index 00000000..8af6ab8f --- /dev/null +++ b/lib/local/CamCom/comet/typelist.h @@ -0,0 +1,450 @@ +/** \file + * Implementation of Lists of types. + * Type lists are rather integral to the operation of Comet, and provide us + * with many of the mechanisms that allow us to all but remove the need for + * \#define macros in code. + */ +/* + * Copyright 2000-2002 Sofus Mortensen, Michael Geddes + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_TYPELIST_H +#define COMET_TYPELIST_H + +#include + +namespace comet { + + /** \struct nil typelist.h comet/typelist.h + * End of list type. + */ + struct nil {}; + + template + struct tl_t { + typedef T head; + typedef U tail; + }; + +#ifndef COMET_TL_TRUNC +#ifndef __BORLANDC__ +#define COMET_TL_LIST( X01,X02,X03,X04,X05,X06,X07,X08,X09, \ + X10,X11,X12,X13,X14,X15,X16,X17,X18,X19, \ + X20,X21,X22,X23,X24,X25,X26,X27,X28,X29, \ + X30,X31,X32,X33,X34,X35,X36,X37,X38,X39) \ + X01,X02,X03,X04,X05,X06,X07,X08,X09, \ + X10,X11,X12,X13,X14,X15,X16,X17,X18,X19, \ + X20,X21,X22,X23,X24,X25,X26,X27,X28,X29, \ + X30,X31,X32,X33,X34,X35,X36,X37,X38,X39 +#else +#define COMET_TL_LIST( X01,X02,X03,X04,X05,X06,X07,X08,X09, \ + X10,X11,X12,X13,X14,X15,X16,X17,X18,X19, \ + X20,X21,X22,X23,X24,X25,X26,X27,X28,X29, \ + X30,X31,X32,X33,X34,X35,X36,X37,X38,X39) \ + X01,X02,X03,X04,X05,X06,X07,X08,X09, \ + X10,X11,X12,X13,X14,X15,X16,X17,X18,X19, \ + X20,X21,X22,X23,X24,X25,X26,X27,X28,X29 +#endif +#else +#define COMET_TL_LIST( X01,X02,X03,X04,X05,X06,X07,X08,X09, \ + X10,X11,X12,X13,X14,X15,X16,X17,X18,X19, \ + X20,X21,X22,X23,X24,X25,X26,X27,X28,X29, \ + X30,X31,X32,X33,X34,X35,X36,X37,X38,X39) \ + X01,X02,X03,X04,X05,X06,X07,X08,X09,X10 +#endif + + + +#define COMET_LIST_TEMPLATE \ + typename X00=comet::nil, COMET_TL_LIST( typename X01=comet::nil, typename X02=comet::nil, typename X03=comet::nil, typename X04=comet::nil, \ + typename X05=comet::nil, typename X06=comet::nil, typename X07=comet::nil, typename X08=comet::nil, typename X09=comet::nil, \ + typename X10=comet::nil, typename X11=comet::nil, typename X12=comet::nil, typename X13=comet::nil, typename X14=comet::nil, \ + typename X15=comet::nil, typename X16=comet::nil, typename X17=comet::nil, typename X18=comet::nil, typename X19=comet::nil, \ + typename X20=comet::nil, typename X21=comet::nil, typename X22=comet::nil, typename X23=comet::nil, typename X24=comet::nil, \ + typename X25=comet::nil, typename X26=comet::nil, typename X27=comet::nil, typename X28=comet::nil, typename X29=comet::nil, \ + typename X30=comet::nil, typename X31=comet::nil, typename X32=comet::nil, typename X33=comet::nil, typename X34=comet::nil, \ + typename X35=comet::nil, typename X36=comet::nil, typename X37=comet::nil, typename X38=comet::nil, typename X39=comet::nil) + +#define COMET_LIST_TEMPLATE_0 typename X00, COMET_TL_LIST(\ + typename X01, typename X02, typename X03, typename X04, \ + typename X05, typename X06, typename X07, typename X08, typename X09, \ + typename X10, typename X11, typename X12, typename X13, typename X14, \ + typename X15, typename X16, typename X17, typename X18, typename X19, \ + typename X20, typename X21, typename X22, typename X23, typename X24, \ + typename X25, typename X26, typename X27, typename X28, typename X29, \ + typename X30, typename X31, typename X32, typename X33, typename X34, \ + typename X35, typename X36, typename X37, typename X38, typename X39) + +#define COMET_LIST_ARG_1 X00, COMET_TL_LIST(\ + X01,X02,X03,X04,X05,X06,X07,X08,X09, \ + X10,X11,X12,X13,X14,X15,X16,X17,X18,X19, \ + X20,X21,X22,X23,X24,X25,X26,X27,X28,X29, \ + X30,X31,X32,X33,X34,X35,X36,X37,X38,X39) + +#define COMET_LIST_ARG_0 COMET_TL_LIST(\ + X01,X02,X03,X04,X05,X06,X07,X08,X09, \ + X10,X11,X12,X13,X14,X15,X16,X17,X18,X19, \ + X20,X21,X22,X23,X24,X25,X26,X27,X28,X29, \ + X30,X31,X32,X33,X34,X35,X36,X37,X38,X39) + +#define COMET_LIST_NIL comet::nil, COMET_TL_LIST(\ + comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil, \ + comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil, \ + comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil, \ + comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil,comet::nil) + + /** \struct make_list typelist.h comet/typelist.h + * Construct a 'type list' of up to 40 types. + * A list is a 'head-tail' list terminated by a 'nil' tail struct. + * It is most easily constructe by using make_list: + \code + comet::make_list< FooType,BarType>::result + \endcode + + * The \e concept of a type-list is that it is either \link comet::nil nil \endlink + * or a struct containing a \e typedef of \p head to the first element in the + * list and a typedef of \p tail to a type-list that contains the rest of + * the list. + * + * This means that for a type-list \e LST + * \code + LST::head + \endcode + * is the first element in the list (assuming it is not + * \link comet::nil nil \endlink) and + * \code + LST::head::tail::head + \endcode + would be the second element in the list (assuming LST::head::tail is not + \link comet::nil nil \endlink), and so on. + + * Two type lists can be appended together with typelist::append. + + \sa typelist::append + */ + template struct make_list + { + typedef tl_t::result> result; + }; + + template<> struct make_list + { + typedef nil result; + }; + + namespace typelist { + + template struct length + { + enum { value = 1 + length< typename L::tail>::value }; + }; + + template<> struct length + { + enum { value = 0 }; + }; + +#ifdef COMET_PARTIAL_SPECIALISATION + //! Find the type at the specified index. + /** \param L Type List + * \param idx Index + */ + template struct type_at + { + typedef typename type_at::result result; + }; + template struct type_at + { + typedef typename L::head result; + }; + template< unsigned idx> struct type_at + { + typedef nil result; + }; + template<> struct type_at + { + typedef nil result; + }; + + + /** \internal + */ + template struct index_of_aux + { + enum { value = 1+(index_of_aux::value) }; + }; + template< typename L, typename T> struct index_of_aux + { + enum { value = 0 }; + }; + /** Find the index of the type \a T in the type-list \a L. + * The result is in index_of::value. + */ + template struct index_of + { + enum { value = index_of_aux::value }; + }; + + +#else + template struct type_at; + + template struct type_at_aux + { + template struct X + { + typedef typename type_at::result result; +// typedef type_at_aux::X::result result; + }; + + template<> struct X<0> + { + typedef typename L::head result; + }; + }; + + template<> struct type_at_aux + { + template struct X + { + typedef nil result; + }; + }; + + +/* template<> struct type_at_aux< make_list<> > + { + template struct X + { + typedef nil result; + }; + };*/ + + template struct type_at + { + typedef typename type_at_aux::X::result result; + }; + + template struct index_of; + + template struct index_of_aux + { + template struct X1 + { + template struct X2 + { + enum { value = 1 + index_of::value }; + }; + + template<> struct X2 + { + enum { value = 0 }; + }; + }; + }; + + template struct index_of + { + enum { value = index_of_aux::X1::X2::value }; + }; +#endif // COMET_PARTIAL_SPECIALISATION + +#undef COMET_LIST_ARG + +#ifdef COMET_PARTIAL_SPECIALISATION + + /** \struct append typelist.h comet/typelist.h + * Appends two type-lists together. + * Example: + * \code + comet::typelist::append< + comet::make_list< Type1, Type2 >::result, + comet::make_list< Type3, Type4 >::result + > + \endcode + \sa make_list + */ + template struct append + { + typedef typename L1::head head; + typedef append tail; + }; + + template struct append + { + typedef typename L2::head head; + typedef typename L2::tail tail; + }; + template struct append,L2> + { + typedef typename L2::head head; + typedef typename L2::tail tail; + }; + template<> struct append + { + typedef nil head; + typedef nil tail; + }; + template struct append_element + { + typedef typename append::result>::head head; + typedef typename append::result>::tail tail; + }; +#else // COMET_PARTIAL_SPECIALISATION + + template struct append; + + namespace impl { + + template struct append_aux + { + template struct with + { + typedef typename L1::head head; + typedef typename append tail; +// typedef typename append_aux::with tail; + }; + + template<> struct with + { + typedef typename L1::head head; + typedef typename L1::tail tail; + }; + }; + + template<> struct append_aux + { + template struct with + { + typedef typename L2::head head; + typedef typename L2::tail tail; + }; + }; + +/* template<> struct append_aux > + { + template struct with + { + typedef typename L2::head head; + typedef typename L2::tail tail; + }; + };*/ + + } + + template struct append + { + typedef typename impl::append_aux::with::head head; + typedef typename impl::append_aux::with::tail tail; + }; + + template struct append_element + { + typedef typename impl::append_aux::with< typename make_list::result >::head head; + typedef typename impl::append_aux::with< typename make_list::result >::tail tail; + }; +#endif // COMET_PARTIAL_SPECIALISATION + + +#ifdef COMET_NESTED_TEMPLATES + /** \struct wrap_each typelist.h comet/typelist.h + * Wrap each of the elements of a list in the specified template class. + * Due to lack of support for template parameter to templates by Microsoft, + * macros have been defined as a necessary evil to get arround this. + * The macros are #COMET_WRAP_EACH_DECLARE and #COMET_WRAP_EACH. + * There is a propper implementation for compilers that support nested + * interfaces, which will be selected by the macro as appropriate. + * \sa COMET_WRAP_EACH_DECLARE COMET_WRAP_EACH + */ + template< template< typename > class T, typename L1> struct wrap_each; + namespace impl + { + template< template< typename > class T, typename L1> struct wrap_each_aux + { + typedef wrap_each x; + }; + template< template< typename > class T> struct wrap_each_aux< T, nil> + { + typedef nil x; + }; + } + + template< template< typename > class T, typename L1> struct wrap_each + { + typedef T head; + typedef typename impl::wrap_each_aux::x tail; + }; + + template< template< typename > class T> struct wrap_each< T, ::comet::nil > + { + }; + /** \def COMET_WRAP_EACH_DECLARE(T) + * Declare a template for wrapping each element in the specified type. + * \relates wrap_each + */ +#define COMET_WRAP_EACH_DECLARE(T) +// MSVC7.1 doesn't like this: +// template< template< typename > class T1, typename L1> struct wrap_each; + + /** \def COMET_WRAP_EACH(T, L1) + * Produce a list L2 that consists of each member of list L1 wrapped in type T. + * This is very much a Lambda calculus concept for those who care. + * \relates wrap_each + */ +#define COMET_WRAP_EACH(T, L1) typelist::wrap_each + +#else // COMET_NESTED_TEMPLATES +#define COMET_WRAP_EACH_DECLARE(T) \ + template< typename L1 > struct wrap_each_aux##T; \ + namespace impl \ + { \ + template< typename L1> struct p_wrap_each_aux##T \ + { \ + typedef wrap_each_aux##T x; \ + }; \ + template<> struct p_wrap_each_aux##T < ::comet::nil> \ + { \ + typedef ::comet::nil x; \ + }; \ + }; \ + template< typename L1> struct wrap_each_aux##T \ + { \ + typedef T head; \ + typedef typename impl::p_wrap_each_aux##T < typename L1::tail>::x tail;\ + }; \ + template<> struct wrap_each_aux##T< ::comet::nil> { }; + +#define COMET_WRAP_EACH(T, L1) wrap_each_aux##T + +#endif // COMET_NESTED_TEMPLATES + + template struct ATL_NO_VTABLE inherit_all + : public ITF_LIST::head, public inherit_all + {}; + + template<> struct inherit_all {}; +// template<> struct inherit_all > {}; + + } + +/* MSVC7 chokes on this one - not actually used anywhere + + template struct select + { + typedef typelist::type_at< make_list::result, idx>::result result; + };*/ + +} // namespace + +#endif diff --git a/lib/local/CamCom/comet/unittest.h b/lib/local/CamCom/comet/unittest.h new file mode 100644 index 00000000..d53821a5 --- /dev/null +++ b/lib/local/CamCom/comet/unittest.h @@ -0,0 +1,86 @@ +/** \file + * Unit-test wrapper. + */ +/* + * Copyright 2000, 2001 Paul Hollingsworth + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_UNITTEST_H +#define COMET_UNITTEST_H + +#include +#include + +namespace comet { + //! Define a unit test \a n. + template + struct test + { + void run() {} + }; + + int current_test = 0; + + //! Run \a n tests. + /** \sa test + */ + template + struct tester + { + inline static void runtests() + { + tester::runtests(); + current_test = n; + test().run(); + } + }; + + template<> + struct tester<0> + { + static void runtests() {} + }; +} // namespace comet + +//! Implement Main function for testing. +template +struct main_t +{ + /// Call to run tests. + static int call(int argc, const char * const argv[]) + { + using std::cout; + using std::cerr; + using std::endl; + try { + comet::tester<64>::runtests(); + } catch (const std::exception &e) { + std::cerr << "Test #" << comet::current_test << " failed - error message: <" << e.what() << ">" << endl; + return 1; + } + catch(...) { + std::cerr << "Test #" << comet::current_test << " failed with an unrecognized exception" << endl; + return 2; + } + std::cout << "Ran all tests successfully" <::call(argc,argv);\ +} + +#endif diff --git a/lib/local/CamCom/comet/util.h b/lib/local/CamCom/comet/util.h new file mode 100644 index 00000000..0e514e09 --- /dev/null +++ b/lib/local/CamCom/comet/util.h @@ -0,0 +1,125 @@ +/** \file + * Various utilities. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_UTIL_H +#define COMET_UTIL_H + +#include + +#include + +#include +#include + +namespace comet { + + /*! \addtogroup WinUtil + */ + //@{ + //! Automatic handling of CoInitialize / CoUninitialize. + class auto_coinit { + public: +#if (_WIN32_WINNT >= 0x0400 ) || defined(_WIN32_DCOM) // DCOM + explicit auto_coinit(COINIT ci) { + ::CoInitializeEx(0, ci) | raise_exception; + } +#endif + + auto_coinit() { ::CoInitialize(0) | raise_exception; } + + ~auto_coinit() { ::CoUninitialize(); } + + private: + auto_coinit& operator=(const auto_coinit&); + auto_coinit(const auto_coinit&); + }; + + // For backward compatibility. + typedef auto_coinit auto_CoInitialize; + + /// Returns the class id of the free threaded marshaler + inline CLSID get_clsid_of_ftm() + { + CLSID rv; + class Dummy : public static_object<> {} dummy; + + IUnknown* pUnk = 0; + ::CoCreateFreeThreadedMarshaler(&dummy, &pUnk) | raise_exception; + + com_ptr marshal = try_cast(pUnk); + + marshal.raw()->GetUnmarshalClass(IID_IUnknown, &dummy, MSHCTX_INPROC,0,MSHLFLAGS_NORMAL, &rv) | raise_exception; + return rv; + } + + /** + * Returns true if and only if object is aggregating the free threaded marshaler. + */ + inline bool is_object_aggregating_ftm(const com_ptr& p) + { + com_ptr marshal = com_cast(p); + if (marshal == 0) return false; + + class Dummy : public static_object<> {} dummy; + + CLSID clsid = CLSID_NULL; + marshal.raw()->GetUnmarshalClass(IID_IUnknown, &dummy, MSHCTX_INPROC,0,MSHLFLAGS_NORMAL, &clsid); + + CLSID ftm_clsid = get_clsid_of_ftm(); + return clsid == ftm_clsid ? true : false; + } + +#if (_WIN32_WINNT >= 0x0400 ) || defined(_WIN32_DCOM) // DCOM + /** + * Returns true if and only if current apartment is the MTA. + */ + inline bool is_mta() + { + if (SUCCEEDED(::CoInitializeEx(0, COINIT_MULTITHREADED))) + { + ::CoUninitialize(); + return true; + } + return false; + } +#endif + /** + * Returns true if and only if current apartment is an STA. + */ + inline bool is_sta() + { + if (SUCCEEDED(::CoInitialize(0))) + { + ::CoUninitialize(); + return true; + } + return false; + } + + /** + * Returns true if and only if specified ptr is a standard proxy. + */ + inline bool is_std_proxy(const com_ptr& unk) + { + com_ptr pm; + return SUCCEEDED(unk.raw()->QueryInterface(IID_IProxyManager, reinterpret_cast(pm.out()))); + } + +} // namespace + +#endif diff --git a/lib/local/CamCom/comet/uuid.h b/lib/local/CamCom/comet/uuid.h new file mode 100644 index 00000000..ffd98024 --- /dev/null +++ b/lib/local/CamCom/comet/uuid.h @@ -0,0 +1,88 @@ +/** \file + * uuid wrapper class. + */ +/* + * Copyright 2001, 2002 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_UUID_H +#define COMET_UUID_H + +#include +#include +#include + +#pragma comment( lib, "Rpcrt4" ) + +namespace comet { + + template + void uuid_t::copy_to_str(C s[36]) const throw() + { + const unsigned char *p = reinterpret_cast(this); + + for (int i=0; i<20; ++i) + { + int j = uuid_table()[i]; + if (j >= 0) + { + const unsigned char byt = p[j]; + *s = hex_table()[byt >> 4]; + ++s; + *s = hex_table()[byt & 0xf]; + } + else *s = L'-'; + ++s; + } + } + + template + bool uuid_t::init_from_str(const C s[], size_t len) throw() + { + unsigned char *p = reinterpret_cast(this); + + bool has_brace; + switch (len) + { + default: return false; + case 36: has_brace = false; break; + case 38: + if (*s != C('{')) + return false; + has_brace = true; + ++s; + break; + } + + int i; + for (i=0; i<20; ++i) + { + int j = uuid_table()[i]; + if (j >= 0) + { + int a = parse_nibble(*s); + ++s; + int b = parse_nibble(*s); + p[j] = unsigned char(a << 4 | b); + } + else if (*s != C('-')) + return false; + ++s; + } + return (! has_brace) || (*s == C('}')); + } + +} + +#endif diff --git a/lib/local/CamCom/comet/uuid_fwd.h b/lib/local/CamCom/comet/uuid_fwd.h new file mode 100644 index 00000000..316178f8 --- /dev/null +++ b/lib/local/CamCom/comet/uuid_fwd.h @@ -0,0 +1,441 @@ +/** \file + * uuid wrapper class. + */ +/* + * Copyright 2001, 2002 Sofus Mortensen + * Copyright 2013 Alexander Lamaison + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_UUID_FWD_H +#define COMET_UUID_FWD_H + +#include +#include + +#include +#include +#include +#include +#include + +namespace comet{ + + +/*! \addtogroup COMType + */ +//@{ + +/// UUID wrapper. +class uuid_t : public COMET_GUID_BASE +{ + struct unitialized_t {}; + uuid_t(unitialized_t) {} + + friend class bstr_t; + +public: + /// Default constructor - constructs a nil uuid. + uuid_t() + { + UuidCreateNil(this); + } + + /// Copy constructor + uuid_t(const COMET_GUID_BASE& u) + { + memcpy(this, &u, sizeof(COMET_GUID_BASE)); + } + + /// Assignment operator + uuid_t& operator=(const COMET_GUID_BASE& u) + { + memcpy(this, &u, sizeof(COMET_GUID_BASE)); + return *this; + } + + /// \name Construct uuid from string + //@{ + explicit uuid_t(const std::string& s) + { + if (!init_from_str(s.c_str(), s.length())) + throw std::runtime_error(err_msg()); + } + + explicit uuid_t(const char* s) + { + if (!init_from_str(s, strlen(s))) + throw std::runtime_error(err_msg()); + } + + explicit uuid_t(const std::wstring& s) + { + if (!init_from_str(s.c_str(), s.length())) + throw std::runtime_error(err_msg()); + } + + explicit uuid_t(const wchar_t* s) + { + if (!init_from_str(s, wcslen(s))) + throw std::runtime_error(err_msg()); + } + + explicit uuid_t(const bstr_t& bs); + //@} + + //! Generate new guid. + static uuid_t create() + { + unitialized_t x; + uuid_t g(x); + UuidCreate(&g); + return g; + } + + //! Set to nil uuid. + void clear() + { + UuidCreateNil(this); + } + + //! Returns hash of uuid + unsigned short hash() const + { + RPC_STATUS status; + unsigned short r = UuidHash(const_cast(this), &status); + if (status != RPC_S_OK) + throw std::runtime_error("uuid_t::hash - UuidHash failed."); + return r; + } + + //! Convert uuid to string + //@{ + /// Return a std::string version of the uuid. + std::string str() const + { + std::string r; + r.resize(36); + copy_to_str(&r[0]); + return r; + } + /// Return a std::string version of the uuid. + inline std::string s_str() const + { + return str(); + } + + /// Return a std::wstring version of the uuid. + std::wstring w_str() const + { + std::wstring r; + r.resize(36); + copy_to_str(&r[0]); + return r; + } + + /// Return a tstring (templated to TCHAR) version of the uuid. + tstring t_str() const + { + tstring r; + r.resize(36); + copy_to_str(&r[0]); + return r; + } + //@} + + /// Returns true if and only if uuid is nil. + bool is_null() const throw() + { + return ((PLONG) this)[0] == 0 && + ((PLONG) this)[1] == 0 && + ((PLONG) this)[2] == 0 && + ((PLONG) this)[3] == 0; + } + + //! Returns true if and only if uuid is nil + bool operator!() const throw() + { + return is_null(); + } + + /// Output to an ostream. + template + friend inline std::basic_ostream& operator<<(std::basic_ostream& os, const uuid_t& u); + + /// Input from an ostream + template + friend inline std::basic_istream& operator>>(std::basic_istream& is, uuid_t& u); + + /** \internal + */ + class bool_tester + { void operator delete(void*); }; + + /// Copy the uuid_t to a string/container. + template void copy_to_str(C s[]) const throw(); + /// Initialise the uuid_t from a string/container. + template bool init_from_str(const C s[], size_t len) throw(); + + static const int* uuid_table() + { + static const int table[] = { 3, 2, 1, 0, -1, 5, 4, -1, 7, 6, -1, 8, 9, -1, 10, 11, 12, 13, 14, 15 }; + return table; + } + + static const char* hex_table() + { + static const char hex[] = "0123456789abcdef"; + return hex; + } + + static const char* err_msg() + { + static const char msg[] = "uuid_t: invalid format"; + return msg; + } + + static int parse_nibble(int c) + { + return c >= 'A' ? (c|0x20) - 'a' + 10 : c - '0'; + } + + /// Compare two guids. + inline long cmp(const comet::uuid_t &rhs) const + { + long i; // The code below may be a bit dense, but it is compact and works. + if((i = (Data1 - rhs.Data1)) == 0 && (i = (Data2 - rhs.Data2)) == 0 && (i = (Data3 - rhs.Data3)) == 0) + (i = ::memcmp(Data4, rhs.Data4, 8)); + return i; + } + + /// Compare two guids for equality. + inline bool is_equal( const comet::uuid_t &rhs) const + { + // From ATL + return ((PLONG) this)[0] == ((PLONG) &rhs)[0] && + ((PLONG) this)[1] == ((PLONG) &rhs)[1] && + ((PLONG) this)[2] == ((PLONG) &rhs)[2] && + ((PLONG) this)[3] == ((PLONG) &rhs)[3]; + } + +public: + /**\internal + */ + operator bool_tester*() const throw() + { if (is_null()) return 0; static bool_tester test; return &test; } + + /// Construct const reference to uuid from a raw GUID + static const uuid_t& create_const_reference(const COMET_GUID_BASE& x) + { + return *reinterpret_cast(&x); + } + + /// Construct reference to uuid from a raw GUID + static uuid_t& create_reference(COMET_GUID_BASE& x) + { + return *reinterpret_cast(&x); + } + + //! \name Methods for converting uuid to raw GUID when calling raw COM functions or legacy code. + //@{ + COMET_GUID_BASE* out() { return this; } + COMET_GUID_BASE* inout() { return this; } + COMET_GUID_BASE in() const { return *this; } + COMET_GUID_BASE* in_ptr() const { return const_cast(static_cast(this)); } + //@} +}; +//@} + +/// \name comparison operators +//@{ +/** Equals operator. + * \relates uuid_t + */ +inline bool operator==(const comet::uuid_t& lhs, const comet::uuid_t& rhs) +{ + return lhs.is_equal(rhs); +} + +/** Inequality operator. + * \relates uuid_t + */ +inline bool operator!=(const comet::uuid_t& lhs, const comet::uuid_t& rhs) +{ + return !lhs.is_equal(rhs); +} + +/** Equals operator. + * \relates uuid_t + */ +inline bool operator==(const comet::uuid_t& lhs, const COMET_GUID_BASE& rhs) +{ + return lhs.is_equal( comet::uuid_t::create_const_reference(rhs) ); +} + +/** Inequality operator. + * \relates uuid_t + */ +inline bool operator!=(const comet::uuid_t& lhs, const COMET_GUID_BASE& rhs) +{ + return !lhs.is_equal( comet::uuid_t::create_const_reference(rhs) ); +} + +/** Equals operator. + * \relates uuid_t + */ +inline bool operator==(const COMET_GUID_BASE& lhs, const comet::uuid_t& rhs) +{ + return comet::uuid_t::create_const_reference(lhs).is_equal(rhs); +} + +/** Inequality operator. + * \relates uuid_t + */ +inline bool operator!=(const COMET_GUID_BASE& lhs, const comet::uuid_t& rhs) +{ + return !comet::uuid_t::create_const_reference(lhs).is_equal(rhs); +} + +/** Less-than operator. + * \relates uuid_t + */ +inline bool operator<(const comet::uuid_t& lhs, const comet::uuid_t& rhs) +{ + return lhs.cmp(rhs)<0; +} + +/** Less-than operator. + * \relates uuid_t + */ +inline bool operator<(const comet::uuid_t& lhs, const COMET_GUID_BASE& rhs) +{ + return lhs.cmp(comet::uuid_t::create_const_reference(rhs))<0; +} + +/** Less-than operator. + * \relates uuid_t + */ +inline bool operator<(const COMET_GUID_BASE& lhs, const comet::uuid_t& rhs) +{ + return comet::uuid_t::create_const_reference(lhs).cmp(rhs)<0; +} + +/** Greater-than-equals operator. + * \relates uuid_t + */ +inline bool operator>=(const comet::uuid_t& lhs, const comet::uuid_t& rhs) +{ + return lhs.cmp(rhs)>=0; +} + +/** Greater-than-equals operator. + * \relates uuid_t + */ +inline bool operator>=(const comet::uuid_t& lhs, const COMET_GUID_BASE& rhs) +{ + return lhs.cmp(comet::uuid_t::create_const_reference(rhs))>=0; +} + +/** Greater-than-equals operator. + * \relates uuid_t + */ +inline bool operator>=(const COMET_GUID_BASE& lhs, const comet::uuid_t& rhs) +{ + return comet::uuid_t::create_const_reference(lhs).cmp(rhs)>=0; +} + +/** Greater-than operator. + * \relates uuid_t + */ +inline bool operator>(const comet::uuid_t& lhs, const comet::uuid_t& rhs) +{ + return lhs.cmp(rhs)>0; +} + +/** Greater-than operator. + * \relates uuid_t + */ +inline bool operator>(const comet::uuid_t& lhs, const COMET_GUID_BASE& rhs) +{ + return lhs.cmp(comet::uuid_t::create_const_reference(rhs))>0; +} + +/** Greater-than operator. + * \relates uuid_t + */ +inline bool operator>(const COMET_GUID_BASE& lhs, const comet::uuid_t& rhs) +{ + return comet::uuid_t::create_const_reference(lhs).cmp(rhs)>0; +} + +/** Less-than-equals operator. + * \relates uuid_t + */ +inline bool operator<=(const comet::uuid_t& lhs, const comet::uuid_t& rhs) +{ + return lhs.cmp(rhs)<=0; +} + +/** Less-than-equals operator. + * \relates uuid_t + */ +inline bool operator<=(const comet::uuid_t& lhs, const COMET_GUID_BASE& rhs) +{ + return lhs.cmp(comet::uuid_t::create_const_reference(rhs))<=0; +} + +/** Less-than-equals operator. + * \relates uuid_t + */ +inline bool operator<=(const COMET_GUID_BASE& lhs, const comet::uuid_t& rhs) +{ + return comet::uuid_t::create_const_reference(lhs).cmp(rhs)<=0; +} +//@} + +/// \name overloads for output/input to/from streams +/// @{ + +/** Output to an ostream. + * \relates uuid_t + */ +template +inline std::basic_ostream& operator<<(std::basic_ostream& os, const comet::uuid_t& u) +{ + E buf[36]; + u.copy_to_str(buf); + os.write(buf, 36); + return os; +} + +/** Input from an istream. + * \relates uuid_t + */ +template +inline std::basic_istream& operator>>(std::basic_istream& is, comet::uuid_t& u) +{ + typename std::basic_istream::sentry se(is); + if (se) + { + E buf[36]; + is.read(buf, 36); + if (!u.init_from_str(buf, is.gcount())) + is.setstate(std::ios::badbit); + } + return is; +} +//@} + +} // namespace comet + +#endif diff --git a/lib/local/CamCom/comet/variant.h b/lib/local/CamCom/comet/variant.h new file mode 100644 index 00000000..95129e24 --- /dev/null +++ b/lib/local/CamCom/comet/variant.h @@ -0,0 +1,1054 @@ +/** \file + * Wrapper for VARIANT. + */ +/* + * Copyright 2000, 2001 Sofus Mortensen, Michael Geddes + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_VARIANT_H +#define COMET_VARIANT_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#pragma warning(push) +#pragma warning(disable : 4127) + +#define COMET_VARIANT_OPERATOR(op, name) \ + variant_t operator##op(const variant_t& x) const \ + { \ + VARIANT t; \ + Var##name(const_cast(get_var()), const_cast(x.get_var()), &t) | raise_exception; \ + return auto_attach(t); \ + } \ + \ + variant_t& operator##op##=(const variant_t& x) \ + { \ + return operator=(operator##op(x)); \ + } + +#define COMET_VARIANT_CONVERTERS_EX_(type, vartype, func) \ + variant_t(type x) throw() \ + { \ + V_##vartype(this) = x; V_VT(this) = VT_##vartype; \ + } \ + \ + func() const \ + { \ + if (V_VT(this) == VT_##vartype) return V_##vartype(this); \ + variant_t v(*this, VT_##vartype); \ + return V_##vartype(v.get_var()); \ + } \ + \ + variant_t& operator=(type x) throw() \ + { \ + clear(); \ + V_##vartype(this) = x; V_VT(this) = VT_##vartype; \ + return *this; \ + } + +#define COMET_VARIANT_CONVERTERS_EXPLICIT(type, vartype, funcname) COMET_VARIANT_CONVERTERS_EX_(type,vartype, type funcname) +#define COMET_VARIANT_CONVERTERS(type, vartype) COMET_VARIANT_CONVERTERS_EX_(type,vartype, operator type ) + +#define COMET_VARIANT_FRIENDS(type) \ + inline bool operator!=(type x, const variant_t& y) { return y != x; } \ + inline bool operator==(type x, const variant_t& y) { return y == x; } \ + inline bool operator<(type x, const variant_t& y) { return y > x; } \ + inline bool operator<=(type x, const variant_t& y) { return y >= x; } \ + inline bool operator>(type x, const variant_t& y) { return y < x; } \ + inline bool operator>=(type x, const variant_t& y) { return y <= x; } + +namespace comet { + + template class safearray_t; + + namespace impl { + + template + inline HRESULT compare(const T& operand1, const T& operand2) + { + if (operand1 == operand2) + return VARCMP_EQ; + else if (operand1 < operand2) + return VARCMP_LT; + else + return VARCMP_GT; + } + + /** + * Comparison workaround for broken VarCmp. + * + * VarCmp() doesn't work for several of the numeric types + * (VT_I1, VT_INT, VT_UI2, VT_UI4, VT_UINT or VT_UI8) so we have + * to do these ourselves. + * + * @see http://source.winehq.org/WineAPI/VarCmp.html + */ + inline HRESULT var_cmp( + VARIANT* lhs, VARIANT* rhs, LCID lcid, ULONG flags) + { + switch (V_VT(lhs)) + { + case VT_I1: + return compare(V_I1(lhs), V_I1(rhs)); + case VT_INT: + return compare(V_INT(lhs), V_INT(rhs)); + case VT_UI2: + return compare(V_UI2(lhs), V_UI2(rhs)); + case VT_UI4: + return compare(V_UI4(lhs), V_UI4(rhs)); + case VT_UINT: + return compare(V_UINT(lhs), V_UINT(rhs)); + case VT_UI8: + return compare(V_UI8(lhs), V_UI8(rhs)); + default: + return ::VarCmp(lhs, rhs, lcid, flags); + } + } + + }; + + template class com_ptr; + + + /*! \addtogroup COMType + */ + //@{ + + //! Wrapper for VARIANT type. + /** variant_t is exception safe (basic and strong guarantee). + */ + class variant_t : private ::tagVARIANT + { + private: + void init() throw() + { + //::VariantInit(this); + tagVARIANT * x = this; + V_VT(x)= VT_EMPTY; + } + + void create(const VARIANT& v) throw(com_error) + { + HRESULT hr = ::VariantCopy(this, const_cast(&v)); + if (FAILED(hr)) { + ::VariantClear(this); + raise_exception(hr); + } + } + + public: + //! Default constructor + variant_t() throw() + { + init(); + } + + private: + struct tagMissing {}; + + variant_t(const tagMissing&) throw() + { + init(); + vt = VT_ERROR; + scode = DISP_E_PARAMNOTFOUND; + } + + struct tagNothing {}; + + variant_t(const tagNothing&) throw() + { + init(); + vt = VT_DISPATCH; + pdispVal = 0; + } + + struct tagNull {}; + + variant_t(const tagNull&) throw() + { + init(); + vt = VT_NULL; + } + + public: + //! Return a variant constructed as missing. + /** (VT_ERROR with code DISP_E_PARAMNOTFOUND ) + */ + static const variant_t& missing() + { + static tagMissing t; + static variant_t v(t); + return v; + } + + //! Return a variant contructed as nothing. + /** (VT_DISPATCH with value 0) + */ + static const variant_t& nothing() + { + static tagNothing t; + static variant_t v(t); + return v; + } + + //! Return a variant constructed as null (VT_NULL). + static const variant_t& null() + { + static tagNull t; + static variant_t v(t); + return v; + } + + //! Copy constructor + /*! + \exception com_error + Thrown if underlying VariantCopy fails. + */ + variant_t(const variant_t& v) throw(com_error) + { + init(); + create(v); + } + + public: + + //! VariantChangeType Constructor + /*! + Copies variant and changes to specified type. + + \note Unlike the _variant_t of VC, variant_t uses Thread Locale for type changes instead of the user setting. + + \par v + Variant to copy from + \par vartype + Type to change to. + + \exception com_error + Thrown if underlying VariantChangeTypeEx fails. + */ + variant_t(const variant_t& v, VARTYPE vartype) throw(com_error) + { + init(); + if (vartype != V_VT(&v)) + ::VariantChangeTypeEx(get_var(), + const_cast(v.get_var()), + GetThreadLocale(), + 0, vartype) | raise_exception; + else + ::VariantCopy(this, const_cast(v.get_var())); + } + + variant_t(const variant_t& v, VARTYPE vartype, std::nothrow_t) throw(com_error) + { + init(); + if (vartype != V_VT(&v)) + ::VariantChangeTypeEx(get_var(), + const_cast(v.get_var()), + GetThreadLocale(), + 0, vartype); + else + ::VariantCopy(this, const_cast(v.get_var())); + } + + //! Constructor + /*! + \exception com_error + Thrown if underlying VariantCopy fails. + */ + explicit variant_t(const VARIANT& v) throw(com_error) + { + init(); + create(v); + } + + //! Attaching constructor + /*! + Takes ownership of specified VARIANT. + + \param v + auto_attach wrapper variant to attach to the variant_t + */ + variant_t(const impl::auto_attach_t& v) throw() + { + memcpy(this, &const_cast(v.get()), sizeof(VARIANT)); + } + + private: + void clear() COMET_THROWS_ASSERT + { + HRESULT hr = ::VariantClear(this); + COMET_ASSERT(SUCCEEDED(hr)); + /* Avoid C4189 */ hr; + } + + public: + + //! Destructor + /*! + \note Be aware that the underlying call to VariantClear may fail. + But since we are not allowed to throw any exceptions (Otherwise STL containers cannot guarantee exception safety) + from within a + destructor, a failing VariantClear will be ignored. + Instead we assert for success in debug and ignore in release. + */ + ~variant_t() throw() + { + clear(); + } + + /// \name com_ptr conversion. + //@{ + private: + template inline void create(const com_ptr& x) throw(); + + + public: + template variant_t(const com_ptr& x) throw() + { + init(); + create(x); + } + + template + variant_t& operator=(const com_ptr& x) throw() + { + clear(); create(x); return *this; + } + + //@} + + + //!\name bool Conversion + //@{ + variant_t(bool x) throw() { + init(); + V_VT(this) = VT_BOOL; + V_BOOL(this) = x ? COMET_VARIANT_TRUE : COMET_VARIANT_FALSE; + } + + operator bool() const throw() + { + if (V_VT(this) == VT_BOOL) return (V_BOOL(this) != COMET_VARIANT_FALSE); + variant_t v(*this, VT_BOOL); + return (V_BOOL(&v) != COMET_VARIANT_FALSE); + } + + variant_t& operator=(bool x) throw() + { + clear(); + V_VT(this) = VT_BOOL; + V_BOOL(this) = x ? COMET_VARIANT_TRUE : COMET_VARIANT_FALSE; + return *this; + } + + //@} + + //!\name string Conversion + //@{ + variant_t(const bstr_t& s) throw(std::bad_alloc) + { + init(); + bstr_t t(s); + V_BSTR(this) = bstr_t::detach(t); + V_VT(this) = VT_BSTR; + } + + variant_t(const wchar_t* s) throw(std::bad_alloc) + { + init(); + bstr_t t(s); + V_BSTR(this) = bstr_t::detach(t); + V_VT(this) = VT_BSTR; + } + + variant_t(const std::wstring& s) throw(std::bad_alloc) + { + init(); + bstr_t t(s); + V_BSTR(this) = bstr_t::detach(t); + V_VT(this) = VT_BSTR; + } + + variant_t(const std::string& s) throw(std::bad_alloc) + { + init(); + bstr_t bs(s); + V_BSTR(this) = bstr_t::detach(bs); + V_VT(this) = VT_BSTR; + } + + variant_t(const char* x) + { + init(); + V_BSTR(this) = bstr_t::detach(x); + V_VT(this) = VT_BSTR; + } + + /** Attach a BSTR to a variant. + * \code + bstr_t val = L"A large string"; + variant_t(auto_attach(val.detach())); + \endcode + */ + variant_t( const impl::auto_attach_t &bstrVal) + { + V_BSTR(this) = bstrVal.get(); + V_VT(this) = VT_BSTR; + } + +/* operator bstr_t() const + { + if (V_VT(this) == VT_BSTR) return V_BSTR(this); + variant_t v(*this, VT_BSTR); + VARIANT t = v.detach(); + return auto_attach(V_BSTR(&t)); + }*/ + + bstr_t str() const + { + if (V_VT(this) == VT_BSTR) return V_BSTR(this); + if (V_VT(this) == (VT_BSTR | VT_BYREF)) return *V_BSTRREF(this); + if (V_VT(this) == VT_NULL) return bstr_t(); + variant_t v(*this, VT_BSTR); + VARIANT t = v.detach(); + return auto_attach(V_BSTR(&t)); + } + + operator bstr_t() const + { + return str(); + } + + operator std::wstring() const + { + if (V_VT(this) == VT_BSTR) return V_BSTR(this); + if (V_VT(this) == (VT_BSTR | VT_BYREF)) return *V_BSTRREF(this); + if (V_VT(this) == VT_NULL) return std::wstring(); + variant_t v(*this, VT_BSTR); + return V_BSTR(&v) ? std::wstring(V_BSTR(&v)) : std::wstring(); + } + + operator std::string() const + { + return str().s_str(); + } + + variant_t& operator=(const bstr_t& s) + { + variant_t t(s); + swap(t); + return *this; + } + + variant_t& operator=(const wchar_t* s) + { + variant_t t(s); + swap(t); + return *this; + } + + variant_t& operator=(const char* s) + { + variant_t t(s); + swap(t); + return *this; + } + + variant_t& operator=(const std::wstring& s) + { + variant_t t(s); + swap(t); + return *this; + } + + variant_t& operator=(const std::string& s) + { + variant_t t(s); + swap(t); + return *this; + } + + //@} + + //! \name safearray Conversions + //@{ + template + variant_t(const safearray_t &x) + { + safearray_t sa( x ); + V_ARRAY(this) = sa.detach(); + V_VT(this) = (VT_ARRAY | safearray_t::traits::vt); + } + + template + variant_t& operator=(const safearray_t &x) throw() + { + variant_t t(x); + swap(t); + return *this; + } + + /** Allow attaching a SAFEARRAY to a variant. + * \code + * safearray_t array; + * variant_t(auto_attach(array.detach())); + * \endcode + */ + variant_t( const impl::auto_attach_t &psa) + { + V_ARRAY(this) = psa.get(); + VARTYPE vt; + SafeArrayGetVartype( psa.get(), &vt) | raise_exception; + V_VT(this) = VARTYPE(VT_ARRAY | vt) ; + } + + + //@} + + //! \name Numeric Conversions + //@{ + + COMET_VARIANT_CONVERTERS_EXPLICIT(short, I2, as_short); + inline operator short() const { return as_short(); } + COMET_VARIANT_CONVERTERS_EXPLICIT(int, I4, as_int); // Do not use VT_INT, because VariantChangeTypeEx does not support VT_INT. + inline operator int() const { return as_int(); } + COMET_VARIANT_CONVERTERS_EXPLICIT(long, I4, as_long); + inline operator long() const { return as_long(); } + COMET_VARIANT_CONVERTERS_EXPLICIT(LONGLONG, I8, as_longlong); + inline operator LONGLONG() const { return as_longlong(); } + COMET_VARIANT_CONVERTERS_EXPLICIT(float, R4, as_float); + inline operator float() const { return as_float(); } + COMET_VARIANT_CONVERTERS_EXPLICIT(double, R8, as_double); + inline operator double() const { return as_double(); } + // These can't have implicit conversions as they cause confusion when + // assigning some common objects from variant_ts. + COMET_VARIANT_CONVERTERS_EXPLICIT(char, I1, as_char); + COMET_VARIANT_CONVERTERS_EXPLICIT(unsigned char, UI1, as_uchar); + COMET_VARIANT_CONVERTERS_EXPLICIT(unsigned short, UI2, as_ushort); + COMET_VARIANT_CONVERTERS_EXPLICIT(unsigned int, UI4, as_uint); + COMET_VARIANT_CONVERTERS_EXPLICIT(unsigned long, UI4, as_ulong); + COMET_VARIANT_CONVERTERS_EXPLICIT(ULONGLONG, UI8, as_ulonglong); + COMET_VARIANT_CONVERTERS_EXPLICIT(DECIMAL, DECIMAL, as_decimal); + + wchar_t as_wchar_t() const { return as_ushort(); } + //@} + // CONVERTERS(DATE, DATE); + + //! \name Currency Conversions + //@{ + + variant_t(const currency_t &x) throw() + { + V_CY(this) = x.get(); + V_VT(this) = VT_CY; + } + + operator currency_t() const + { + return as_curency(); + } + currency_t as_curency() const + { + if (V_VT(this) == VT_CY) return V_CY(this); + variant_t v(*this, VT_CY); + return V_CY(v.get_var()); + } + + variant_t& operator=(const currency_t &x) throw() + { + clear(); + V_CY(this) = x.get(); + V_VT(this) = VT_CY; + return *this; + } + + //@} + + //! \name Date Conversions + //@{ + + variant_t(const datetime_t &x) throw() + { + V_DATE(this) = x.get(); + V_VT(this) = VT_DATE; + } + + operator datetime_t() const + { + if (V_VT(this) == VT_DATE) return datetime_t(V_DATE(this)); + variant_t v(*this, VT_DATE); + return datetime_t(V_DATE(v.get_var())); + } + + variant_t& operator=(const datetime_t &x) throw() + { + clear(); + V_DATE(this) = x.get(); + V_VT(this) = VT_DATE; + return *this; + } + + //@} + /// swap routine, fast with nothrow guarantee + void swap(variant_t& x) throw() + { + ::tagVARIANT t; + memcpy(&t, this, sizeof(VARIANT)); + memcpy(this, &x, sizeof(VARIANT)); + memcpy(&x, &t, sizeof(VARIANT)); + } + + /// Assignment operator + variant_t& operator=(const variant_t& x) throw(com_error) + { + variant_t t(x); + swap(t); + return *this; + } + + //! \name Comparison operators + //@{ + template + bool operator==(const T& x) const throw(com_error) + { + return operator==( variant_t(x) ); + } + + bool operator==(const variant_t& x) const throw(com_error) + { + if (V_VT(&x) != V_VT(this)) { + if (V_VT(this) == VT_EMPTY || V_VT(&x) == VT_EMPTY) return false; + variant_t tmp(x, V_VT(this), std::nothrow); + if (V_VT(&tmp) != V_VT(this)) return false; + return VARCMP_EQ == (impl::var_cmp(const_cast(get_var()), const_cast(tmp.get_var()), GetThreadLocale(), 0) | raise_exception) ; + } else { + switch (impl::var_cmp(const_cast(get_var()), const_cast(x.get_var()), GetThreadLocale(), 0)) + { + case VARCMP_EQ: + case VARCMP_NULL: + return true; + default: + return false; + } + } + } + + template + bool operator!=(const T& x) const throw(com_error) + { + return !operator==(variant_t(x)); + } + + bool operator!=(const variant_t& x) const throw(com_error) + { + return !operator==(x); + } + + template + bool operator<(const T& x) const throw(com_error) + { + return operator<(variant_t(x)); + } + + bool operator<(const variant_t& x) const throw(com_error) + { + if (V_VT(&x) != V_VT(this)) { + return VARCMP_LT == (impl::var_cmp(const_cast(get_var()), const_cast(variant_t(x, V_VT(this)).get_var()), GetThreadLocale(), 0) | raise_exception); + } else { + return VARCMP_LT == (impl::var_cmp(const_cast(get_var()), const_cast(x.get_var()), GetThreadLocale(), 0) | raise_exception); + } + } + + template + bool operator<=(const T& x) const throw(com_error) + { + return operator<=(variant_t(x)); + } + + template + bool operator>(const T& x) const throw(com_error) + { + return operator>(variant_t(x)); + } + + bool operator>(const variant_t& x) const throw(com_error) + { + if (V_VT(&x) != V_VT(this)) { + return VARCMP_GT == (impl::var_cmp(const_cast(get_var()), const_cast(variant_t(x, V_VT(this)).get_var()), GetThreadLocale(), 0) | raise_exception); + } else { + return VARCMP_GT == (impl::var_cmp(const_cast(get_var()), const_cast(x.get_var()), GetThreadLocale(), 0) | raise_exception); + } + } + + bool operator<=(const variant_t& x) const throw(com_error) + { + return !operator>(x); + } + + template + bool operator>=(const T& x) const throw(com_error) + { + return operator>=(variant_t(x)); + } + + bool operator>=(const variant_t& x) const throw(com_error) + { + return !operator<(x); + } + + //@} + + //! \name Mathematical operators + //@{ + COMET_VARIANT_OPERATOR(+,Add); + COMET_VARIANT_OPERATOR(-,Sub); + COMET_VARIANT_OPERATOR(*,Mul); + COMET_VARIANT_OPERATOR(/,Div); + COMET_VARIANT_OPERATOR(&,And); + COMET_VARIANT_OPERATOR(|,Or); + COMET_VARIANT_OPERATOR(^,Xor); + COMET_VARIANT_OPERATOR(%,Mod); + + variant_t operator-() const + { + VARIANT t; + VarNeg(const_cast(get_var()), &t) | raise_exception; + return auto_attach(t); + } + + void change_type(VARTYPE vartype) throw(com_error) + { + if (vartype != V_VT(this)) + ::VariantChangeTypeEx(get_var(), + get_var(), + GetThreadLocale(), + 0, vartype) | raise_exception; + } + + //@} + + //! Is variant a BSTR + bool is_string() const + { + return VT_BSTR == get_vt(true); + } + + //! Is variant an IDispatch or IUnknown pointer? + bool is_object() const + { + return VT_UNKNOWN == get_vt(true) || VT_DISPATCH == get_vt(true); + } + + /*! Is variant empty. + * This compares strictly to VT_EMPTY. + */ + bool is_empty() const throw() + { + return VT_EMPTY == get_vt(); + } + /*! Is variant 'NULL'. + * This compares strictly to VT_NULL. + */ + bool is_null() const throw() + { + return VT_NULL == get_vt(); + } + + /**! Is variant Nothing. + * Is this a NULL pointer, empty, or null. + * This returns true if the value would cleanly try_cast to a NULL + * com_ptr. + */ + bool is_nothing() const throw() + { + switch (get_vt()) { + case VT_DISPATCH: return NULL == V_DISPATCH(&get()); + case VT_UNKNOWN: return NULL == V_UNKNOWN(&get()); + case VT_DISPATCH|VT_BYREF: return NULL == *V_DISPATCHREF(&get()); + case VT_UNKNOWN|VT_BYREF: return NULL == *V_UNKNOWNREF(&get()); + case VT_EMPTY: + case VT_NULL: return true; + } + return false; + } + + + //! \name Accessor Functions + //@{ + const VARIANT& get() const throw() + { + return *get_var(); + } + + VARTYPE get_vt() const throw() + { + return VARTYPE(V_VT(this)); + } + + VARTYPE get_vt(bool ignore_byref) const throw() + { + return ignore_byref ? VARTYPE(V_VT(this) & ~VT_BYREF) : VARTYPE(V_VT(this)); + } + + static VARIANT detach(variant_t& v) throw() + { + return v.detach(); + } + + VARIANT detach() throw() + { + VARIANT r = *get_var(); + V_VT(this) = VT_EMPTY; + return r; + } + + static const variant_t& create_const_reference(const VARIANT& x) + { + return *reinterpret_cast(&x); + } + + static variant_t& create_reference(VARIANT& x) + { + return *reinterpret_cast(&x); + } + + //! [in] adapter. + /*! + Used when calling raw interfaces that require an [in] VARIANT argument. + + \code + variant_t v; + HRESULT hr = pRawInterface->raw_Method(v.in()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + VARIANT in() const throw() + { + return *get_var(); + } + + //! [in] adapter. + /*! + Used when calling raw interfaces that require an [in] VARIANT* argument. + + \code + variant_t v; + HRESULT hr = pRawInterface->raw_Method(v.in_ptr()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + VARIANT* in_ptr() const throw() + { + return const_cast(get_var()); + } + + //! [out] adapter. + /*! + Used when calling raw interfaces that require an [out] VARIANT * argument. + + \code + variant_t v; + HRESULT hr = pRawInterface->raw_MethodThatReturnsVariant(v.out()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + VARIANT* out() throw() + { + clear(); + new (this) variant_t(); + return get_var(); + } + + //! [in, out] adapter. + /*! + Used when calling raw interfaces that require an [in, out] VARIANT * argument. + + \code + variant_t v; + HRESULT hr = pRawInterface->raw_MethodThatChangesVariant(v.inout()); + \endcode + + Only use this wrapper when forced to deal with raw interface. + */ + VARIANT* inout() throw() + { + return get_var(); + } + //@} + + private: +#define __COMET_VARAIANT_OUT(vartype) case VT_##vartype: os << V_##vartype(this); break +#define __COMET_VARAIANT_OUT_CAST(vartype,cast) case VT_##vartype: os << cast(V_##vartype(this)); break + template + std::basic_ostream &output(std::basic_ostream &os) const + { + switch (V_VT(this)) + { + __COMET_VARAIANT_OUT_CAST(I1, short); + __COMET_VARAIANT_OUT(I2); + __COMET_VARAIANT_OUT(I4); + __COMET_VARAIANT_OUT(INT); + __COMET_VARAIANT_OUT_CAST(UI1, (unsigned short)); + __COMET_VARAIANT_OUT(UI2); + __COMET_VARAIANT_OUT(UI4); + __COMET_VARAIANT_OUT(UINT); + __COMET_VARAIANT_OUT(R4); + __COMET_VARAIANT_OUT(R8); +// __COMET_VARAIANT_OUT_CAST(CY, currency_t::create_const_reference); +// __COMET_VARAIANT_OUT_CAST(DATE, datetime_t::create_const_reference); + default: + os << std::basic_string(*this); + break; + } + return os; + } +#undef __COMET_VARAIANT_OUT + public: + friend + std::basic_ostream &operator<<(std::basic_ostream &os, const variant_t &val) + { + return val.output(os); + } + + friend + std::basic_ostream &operator<<(std::basic_ostream &os, const variant_t &val) + { + return val.output(os); + } + + private: + const VARIANT* get_var() const throw() + { +#ifndef __BORLANDC__ + return static_cast(this); +#else + return reinterpret_cast(this); +#endif + } + + VARIANT* get_var() throw() + { +#ifdef __BORLANDC__ +#if __BORLANDC__ >= 0x0551 + return reinterpret_cast(this); +#else + return static_cast(this); +#endif +#else + return reinterpret_cast(this); +#endif + } + }; + //@} + + COMET_VARIANT_FRIENDS(short); + COMET_VARIANT_FRIENDS(int); + COMET_VARIANT_FRIENDS(long); + COMET_VARIANT_FRIENDS(float); + COMET_VARIANT_FRIENDS(double); + + COMET_VARIANT_FRIENDS(const char*); + COMET_VARIANT_FRIENDS(const wchar_t*); + + COMET_VARIANT_FRIENDS(const std::wstring&); + COMET_VARIANT_FRIENDS(const std::string&); + + COMET_VARIANT_FRIENDS(const DECIMAL&); + +} // namespace + +#include +#include + +namespace comet{ + template inline void variant_t::create(const com_ptr& x) throw() + { + com_ptr< ::IDispatch > p( com_cast(x) ); + if (p != 0) { + V_VT(this) = VT_DISPATCH; + V_DISPATCH(this) = p.detach(); + } else { + V_VT(this) = VT_UNKNOWN; +// VTT_(punkVal) = static_cast<::IUnknown*>(com_ptr(x).detach()); + com_ptr< ::IUnknown > p( com_cast(x) ); + V_UNKNOWN(this) = p.detach(); + } + } + +} + +namespace std { + template<> inline void swap(comet::variant_t& x, comet::variant_t& y) COMET_STD_SWAP_NOTHROW + { + x.swap(y); + } +} + +#undef COMET_VARIANT_CONVERTERS +#undef COMET_VARIANT_CONVERTERS_EX_ +#undef COMET_VARIANT_CONVERTERS_EXPLICIT +#undef COMET_VARIANT_OPERATOR +#undef COMET_VARIANT_FRIENDS + +namespace comet { + /*! \addtogroup COMType + */ + //@{ + + inline bool operator!=(const bstr_t& b, const variant_t& v) + { + return v != b; + } + + inline bool operator==(const bstr_t& b, const variant_t& v) + { + return v == b; + } + + inline bool operator<(const bstr_t& b, const variant_t& v) + { + return v > b; + } + + inline bool operator>(const bstr_t& b, const variant_t& v) + { + return v < b; + } + + inline bool operator<=(const bstr_t& b, const variant_t& v) + { + return v >= b; + } + + inline bool operator>=(const bstr_t& b, const variant_t& v) + { + return v <= b; + } + //@} +} + +#pragma warning(pop) + +#endif diff --git a/lib/local/CamCom/comet/variant_iterator.h b/lib/local/CamCom/comet/variant_iterator.h new file mode 100644 index 00000000..618df947 --- /dev/null +++ b/lib/local/CamCom/comet/variant_iterator.h @@ -0,0 +1,149 @@ +/** \file + * Standard C++ iterators wrapping IEnumVARIANT objects. + */ +/* + * Copyright 2000 Sofus Mortensen + * + * This material is provided "as is", with absolutely no warranty + * expressed or implied. Any use is at your own risk. Permission to + * use or copy this software for any purpose is hereby granted without + * fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is + * granted, provided the above notices are retained, and a notice that + * the code was modified is included with the above copyright notice. + * + * This header is part of Comet version 2. + * https://github.com/alamaison/comet + */ + +#ifndef COMET_VARIANT_ITERATOR_H +#define COMET_VARIANT_ITERATOR_H + +#include + +#include +#include + +#include + +namespace comet { + + /** \class variant_iterator enum.h comet/enum.h + * STL style iterator for IEnumVariant interface. + */ + class variant_iterator + { + com_ptr enum_; + variant_t ce_; + public: + /** Constructor. + */ + variant_iterator( const com_ptr& e ) : enum_(e) { + next(); + } + + variant_iterator() {} + + /** Move to next element. + */ + variant_iterator& operator++() { + next(); + return *this; + } + + bool operator!=(const variant_iterator& v) { + if (!v.enum_.is_null()) + throw std::logic_error( + "variant_iterator comparison does not work"); + + return !enum_.is_null(); + } + + /** Move to next element (post increment). + */ + variant_iterator operator++(int) { + variant_iterator t(*this); + operator++(); + return t; + } + + /** Current element. + */ + variant_t& operator*() { + return ce_; + } + private: + void next() { + if (enum_) { + unsigned long x = 0; + enum_->Next(1, ce_.out(), &x) | raise_exception; + if (x == 0) enum_ = 0; + } + } + }; + + /** \class itf_iterator enum.h comet/enum.h + * STL style Iterator for IEnumVARIANT interface returning a contained + * interface pointer. + */ + template class itf_iterator + { + com_ptr enum_; + com_ptr p_; + public: + /** Constructor. + */ + itf_iterator( const com_ptr& e ) : enum_(e) { + next(); + } + + itf_iterator() {} + + /** Move to next element. + */ + itf_iterator& operator++() { + next(); + return *this; + } + + bool operator!=(const itf_iterator& v) { + if (v.enum_) + throw std::logic_error( + "itf_iterator comparison does not work"); + + return enum_ != 0; + } + + /** Move to next element. + */ + itf_iterator operator++(int) { + itf_iterator t(*this); + operator++(); + return t; + } + + /** Access element. + */ + com_ptr& operator*() { + return p_; + } + private: + void next() { + if (enum_) { + unsigned long x = 0; + variant_t v; + enum_->Next(1, v.out(), &x) | raise_exception; + if (x == 0) { + enum_ = 0; + p_ = 0; + } + else { + p_ = try_cast(v); + } + } + } + }; + +} + +#endif diff --git a/lib/local/CamCom/comet_auto_mf.h b/lib/local/CamCom/comet_auto_mf.h new file mode 100644 index 00000000..0e2c1c59 --- /dev/null +++ b/lib/local/CamCom/comet_auto_mf.h @@ -0,0 +1,20 @@ +#pragma once + +#include "comet/handle_except.h" +#include + +namespace comet +{ + struct auto_mf + { + auto_mf(DWORD dwFlags = MFSTARTUP_FULL) + { + MFStartup(MF_VERSION) | comet::raise_exception; + } + ~auto_mf() + { + MFShutdown(); + } + }; +} + diff --git a/lib/local/CamCom/comet_com_ptr_array.h b/lib/local/CamCom/comet_com_ptr_array.h new file mode 100644 index 00000000..17ac4242 --- /dev/null +++ b/lib/local/CamCom/comet_com_ptr_array.h @@ -0,0 +1,65 @@ +#ifndef comet_com_ptr_array_h__ +#define comet_com_ptr_array_h__ + +#include +#include + +namespace comet { + template + class com_ptr_array { + public: + com_ptr_array() : _ptr(nullptr), _count(0) {} + ~com_ptr_array() { + clear(); + } + + + TInf** in() { + return _ptr.in(); + } + + TInf*** inout() { + return _ptr.inout(); + } + + TInf*** out() { + clear(); + return _ptr.out(); + } + + TCount count() { + return _count; + } + + TCount* inout_count() { + return &_count; + } + + TCount* out_count() { + clear(); + return &_count; + } + + com_ptr operator[](size_t i) { + return com_ptr(_ptr[i]); + } + + + + private: + void clear() { + if (_ptr) { + for (DWORD i = 0; i < _count; i++) { + _ptr[i]->Release(); + } + _count = 0; + _ptr.free(); + } + } + + task_ptr _ptr; + TCount _count; + }; +} + +#endif // comet_com_ptr_array_h__ \ No newline at end of file diff --git a/lib/local/CamCom/comet_task_ptr.h b/lib/local/CamCom/comet_task_ptr.h new file mode 100644 index 00000000..dfec3808 --- /dev/null +++ b/lib/local/CamCom/comet_task_ptr.h @@ -0,0 +1,85 @@ +#ifndef comet_task_ptr_h__ +#define comet_task_ptr_h__ + +#include + +namespace comet { + template + class task_ptr { + public: + task_ptr() : _ptr(0) {} + task_ptr(T* ptr) : _ptr(ptr) {} + + task_ptr( const task_ptr& other ); // non construction-copyable + task_ptr( task_ptr&& other ); // non construction-movable + task_ptr& operator=( const task_ptr& ); // non copyable + task_ptr& operator=( task_ptr&& ); // non movable + + ~task_ptr() { + free(); + } + + void free() { + if (_ptr) { + CoTaskMemFree(_ptr); + } + _ptr = 0; + } + + bool alloc(size_t size) { + free(); + _ptr = ::CoTaskMemAlloc(size); + return _ptr != NULL; + } + + bool realloc(size_t size) { + _ptr = ::CoTaskMemRealloc(_ptr, size); + return _ptr != NULL; + } + + T* in() { + return _ptr; + } + + T** inout() { + return &_ptr; + } + + T** out() { + free(); + return &_ptr; + } + +#ifdef _TYPEDEF_BOOL_TYPE + typedef task_ptr _Myt; + _TYPEDEF_BOOL_TYPE; + _OPERATOR_BOOL() const _NOEXCEPT + { // test for non-null pointer + return (_ptr != 0 ? _CONVERTIBLE_TO_TRUE : 0); + } +#else + explicit operator bool() const + { // test for non-null pointer + return _ptr != 0; + } +#endif + + T& operator[](size_t i) { + return _ptr[i]; + } + const T& operator[](size_t i) const { + return _ptr[i]; + } + T* operator->() { + return _ptr; + } + T operator*() { + return _ptr; + } + + private: + T* _ptr; + }; +} + +#endif // comet_task_ptr_h__ diff --git a/lib/local/CamCom/stdafx.cpp b/lib/local/CamCom/stdafx.cpp new file mode 100644 index 00000000..1907c1bf --- /dev/null +++ b/lib/local/CamCom/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// ComCam.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/lib/local/CamCom/stdafx.h b/lib/local/CamCom/stdafx.h new file mode 100644 index 00000000..b005a839 --- /dev/null +++ b/lib/local/CamCom/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/lib/local/CamCom/targetver.h b/lib/local/CamCom/targetver.h new file mode 100644 index 00000000..87c0086d --- /dev/null +++ b/lib/local/CamCom/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/lib/local/CppInerop/AssemblyInfo.cpp b/lib/local/CppInerop/AssemblyInfo.cpp new file mode 100644 index 00000000..700bcb41 --- /dev/null +++ b/lib/local/CppInerop/AssemblyInfo.cpp @@ -0,0 +1,38 @@ +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly:AssemblyTitleAttribute("CppInterop")]; +[assembly:AssemblyDescriptionAttribute("")]; +[assembly:AssemblyConfigurationAttribute("")]; +[assembly:AssemblyCompanyAttribute("")]; +[assembly:AssemblyProductAttribute("CppInterop")]; +[assembly:AssemblyCopyrightAttribute("Copyright (c) 2016")]; +[assembly:AssemblyTrademarkAttribute("")]; +[assembly:AssemblyCultureAttribute("")]; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the value or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly:AssemblyVersionAttribute("1.0.*")]; + +[assembly:ComVisible(false)]; + +[assembly:CLSCompliantAttribute(true)]; + +[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; diff --git a/lib/local/CppInerop/CameraInterop.h b/lib/local/CppInerop/CameraInterop.h new file mode 100644 index 00000000..63faa5a4 --- /dev/null +++ b/lib/local/CppInerop/CameraInterop.h @@ -0,0 +1,462 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED AS IS FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called open source software licenses (Open Source +// Components), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensees request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltruaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltruaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +// Camera_Interop.h + +#pragma once + +#pragma unmanaged + +// Include all the unmanaged things we need. + +#include +#include "opencv2/objdetect.hpp" +#include "opencv2/calib3d.hpp" +#include +#include +#include +#include +#include + +#include + +// For camera listings +#include "comet_auto_mf.h" +#include "camera_helper.h" + +#pragma managed + +#include +#include + +namespace CameraInterop { + + public ref class CaptureFailedException : System::Exception + { + public: + + CaptureFailedException(System::String^ message): Exception(message){} + }; + + public ref class Capture + { + private: + + // OpenCV based video capture for reading from files + cv::VideoCapture* vc; + + OpenCVWrappers::RawImage^ latestFrame; + OpenCVWrappers::RawImage^ grayFrame; + + double fps; + + bool is_webcam; + bool is_image_seq; + + int frame_num; + std::vector* image_files; + + int vid_length; + + public: + + int width, height; + + Capture(int device, int width, int height) + { + assert(device >= 0); + + latestFrame = gcnew OpenCVWrappers::RawImage(); + + vc = new cv::VideoCapture(device); + vc->set(CV_CAP_PROP_FRAME_WIDTH, width); + vc->set(CV_CAP_PROP_FRAME_HEIGHT, height); + + is_webcam = true; + is_image_seq = false; + + this->width = width; + this->height = height; + + vid_length = 0; + frame_num = 0; + + int set_width = vc->get(CV_CAP_PROP_FRAME_WIDTH); + int set_height = vc->get(CV_CAP_PROP_FRAME_HEIGHT); + + if(!vc->isOpened()) + { + throw gcnew CaptureFailedException("Failed to open the webcam"); + } + if(set_width != width || set_height != height) + { + throw gcnew CaptureFailedException("Failed to open the webcam with desired resolution"); + } + } + + Capture(System::String^ videoFile) + { + latestFrame = gcnew OpenCVWrappers::RawImage(); + + vc = new cv::VideoCapture(msclr::interop::marshal_as(videoFile)); + fps = vc->get(CV_CAP_PROP_FPS); + is_webcam = false; + is_image_seq = false; + this->width = vc->get(CV_CAP_PROP_FRAME_WIDTH); + this->height = vc->get(CV_CAP_PROP_FRAME_HEIGHT); + + vid_length = vc->get(CV_CAP_PROP_FRAME_COUNT); + frame_num = 0; + + if(!vc->isOpened()) + { + throw gcnew CaptureFailedException("Failed to open the video file"); + } + } + + // An alternative to using video files is using image sequences + Capture(System::Collections::Generic::List^ image_files) + { + + latestFrame = gcnew OpenCVWrappers::RawImage(); + + is_webcam = false; + is_image_seq = true; + this->image_files = new std::vector(); + + for(int i = 0; i < image_files->Count; ++i) + { + this->image_files->push_back(msclr::interop::marshal_as(image_files[i])); + } + vid_length = image_files->Count; + } + + static System::Collections::Generic::Dictionary^>^>^ GetListingFromFile(std::string filename) + { + // Check what cameras have been written (using OpenCVs XML packages) + cv::FileStorage fs_read(filename, cv::FileStorage::READ); + + auto managed_camera_list_initial = gcnew System::Collections::Generic::Dictionary^>^>(); + + cv::FileNode camera_node_list = fs_read["cameras"]; + + // iterate through a sequence using FileNodeIterator + for(size_t idx = 0; idx < camera_node_list.size(); idx++ ) + { + std::string camera_name = (std::string)camera_node_list[idx]["name"]; + + cv::FileNode resolution_list = camera_node_list[idx]["resolutions"]; + auto resolutions = gcnew System::Collections::Generic::List^>(); + for(size_t r_idx = 0; r_idx < resolution_list.size(); r_idx++ ) + { + int x = (int)resolution_list[r_idx]["x"]; + int y = (int)resolution_list[r_idx]["y"]; + resolutions->Add(gcnew System::Tuple(x, y)); + } + managed_camera_list_initial[gcnew System::String(camera_name.c_str())] = resolutions; + } + fs_read.release(); + return managed_camera_list_initial; + } + + static void WriteCameraListingToFile(System::Collections::Generic::Dictionary^>^>^ camera_list, std::string filename) + { + cv::FileStorage fs("camera_list.xml", cv::FileStorage::WRITE); + + fs << "cameras" << "["; + for each( System::String^ name_m in camera_list->Keys ) + { + + std::string name = msclr::interop::marshal_as(name_m); + + fs << "{:" << "name" << name; + fs << "resolutions" << "["; + auto resolutions = camera_list[name_m]; + for(int j = 0; j < resolutions->Count; j++) + { + + fs << "{:" << "x" << resolutions[j]->Item1 << "y" << resolutions[j]->Item2; + fs<< "}"; + } + fs << "]"; + fs << "}"; + } + fs << "]"; + fs.release(); + } + + static System::Collections::Generic::List^>^, OpenCVWrappers::RawImage^>^>^ GetCameras(System::String^ root_directory_m) + { + std::string root_directory = msclr::interop::marshal_as(root_directory_m); + auto managed_camera_list_initial = GetListingFromFile(root_directory + "camera_list.xml"); + + auto managed_camera_list = gcnew System::Collections::Generic::List^>^, OpenCVWrappers::RawImage^>^>(); + + // Using DirectShow for capturing from webcams (for MJPG as has issues with other formats) + comet::auto_mf auto_mf; + + std::vector cameras = camera_helper::get_all_cameras(); + + // A Surface Pro specific hack, it seems to list webcams in a weird way + for (size_t i = 0; i < cameras.size(); ++i) + { + cameras[i].activate(); + std::string name = cameras[i].name(); + if(name.compare("Microsoft LifeCam Front") == 0) + { + cameras.push_back(cameras[i]); + cameras.erase(cameras.begin() + i); + } + } + + + for (size_t i = 0; i < cameras.size(); ++i) + { + cameras[i].activate(); + std::string name = cameras[i].name(); + System::String^ name_managed = gcnew System::String(name.c_str()); + + // List camera media types + auto media_types = cameras[i].media_types(); + + System::Collections::Generic::List^>^ resolutions; + std::set> res_set; + + // If we have them just use pre-loaded resolutions + if(managed_camera_list_initial->ContainsKey(name_managed)) + { + resolutions = managed_camera_list_initial[name_managed]; + } + else + { + resolutions = gcnew System::Collections::Generic::List^>(); + for (size_t m = 0; m < media_types.size(); ++m) + { + auto media_type_curr = media_types[m]; + res_set.insert(std::pair(std::pair(media_type_curr.resolution().width, media_type_curr.resolution().height))); + } + } + + // Grab some sample images and confirm the resolutions + cv::VideoCapture cap1(i); + // Go through resolutions if they have not been identified + if(resolutions->Count == 0) + { + for (auto beg = res_set.begin(); beg != res_set.end(); ++beg) + { + auto resolution = gcnew System::Tuple(beg->first, beg->first); + + cap1.set(CV_CAP_PROP_FRAME_WIDTH, resolution->Item1); + cap1.set(CV_CAP_PROP_FRAME_HEIGHT, resolution->Item2); + + // Add only valid resolutions as API sometimes provides wrong ones + int set_width = cap1.get(CV_CAP_PROP_FRAME_WIDTH); + int set_height = cap1.get(CV_CAP_PROP_FRAME_HEIGHT); + + resolution = gcnew System::Tuple(set_width, set_height); + if(!resolutions->Contains(resolution)) + { + resolutions->Add(resolution); + } + } + managed_camera_list_initial[name_managed] = resolutions; + } + + cv::Mat sample_img; + OpenCVWrappers::RawImage^ sample_img_managed = gcnew OpenCVWrappers::RawImage(); + + // Now that the resolutions have been identified, pick a camera and create a thumbnail + if(resolutions->Count > 0) + { + int resolution_ind = resolutions->Count / 2; + + if(resolution_ind >= resolutions->Count) + resolution_ind = resolutions->Count - 1; + + auto resolution = resolutions[resolution_ind]; + + cap1.set(CV_CAP_PROP_FRAME_WIDTH, resolution->Item1); + cap1.set(CV_CAP_PROP_FRAME_HEIGHT, resolution->Item2); + + for (int k = 0; k < 5; ++k) + cap1.read(sample_img); + + // Flip horizontally + cv::flip(sample_img, sample_img, 1); + + + } + cap1.~VideoCapture(); + + sample_img.copyTo(sample_img_managed->Mat); + + managed_camera_list->Add(gcnew System::Tuple^>^, OpenCVWrappers::RawImage^>(gcnew System::String(name.c_str()), resolutions, sample_img_managed)); + } + + WriteCameraListingToFile(managed_camera_list_initial, root_directory + "camera_list.xml"); + + return managed_camera_list; + } + + OpenCVWrappers::RawImage^ GetNextFrame(bool mirror) + { + frame_num++; + + if(vc != nullptr) + { + + bool success = vc->read(latestFrame->Mat); + + if (!success) + { + // Indicate lack of success by returning an empty image + cv::Mat empty_mat = cv::Mat(); + empty_mat.copyTo(latestFrame->Mat); + return latestFrame; + } + } + else if(is_image_seq) + { + if(image_files->empty()) + { + // Indicate lack of success by returning an empty image + cv::Mat empty_mat = cv::Mat(); + empty_mat.copyTo(latestFrame->Mat); + return latestFrame; + } + + cv::Mat img = cv::imread(image_files->at(0), -1); + img.copyTo(latestFrame->Mat); + // Remove the first frame + image_files->erase(image_files->begin(), image_files->begin() + 1); + } + + if (grayFrame == nullptr) { + if (latestFrame->Width > 0) { + grayFrame = gcnew OpenCVWrappers::RawImage(latestFrame->Width, latestFrame->Height, CV_8UC1); + } + } + + if(mirror) + { + flip(latestFrame->Mat, latestFrame->Mat, 1); + } + + + if (grayFrame != nullptr) { + cvtColor(latestFrame->Mat, grayFrame->Mat, CV_BGR2GRAY); + } + + return latestFrame; + } + + double GetProgress() + { + if(vc != nullptr && is_webcam) + { + return - 1.0; + } + else + { + return (double)frame_num / (double)vid_length; + } + } + + bool isOpened() + { + if(vc != nullptr) + return vc->isOpened(); + else + { + if(is_image_seq && image_files->size() > 0) + return true; + else + return false; + } + } + + OpenCVWrappers::RawImage^ GetCurrentFrameGray() { + return grayFrame; + } + + double GetFPS() { + return fps; + } + + // Finalizer. Definitely called before Garbage Collection, + // but not automatically called on explicit Dispose(). + // May be called multiple times. + !Capture() + { + // Automatically closes capture object before freeing memory. + if(vc != nullptr) + { + vc->~VideoCapture(); + } + if(image_files != nullptr) + delete image_files; + } + + // Destructor. Called on explicit Dispose() only. + ~Capture() + { + this->!Capture(); + } + }; + +} diff --git a/lib/local/CppInerop/CppInerop.vcxproj b/lib/local/CppInerop/CppInerop.vcxproj new file mode 100644 index 00000000..0dd42b23 --- /dev/null +++ b/lib/local/CppInerop/CppInerop.vcxproj @@ -0,0 +1,205 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {78196985-EE54-411F-822B-5A23EDF80642} + Win32Proj + CppInerop + 8.1 + + + + DynamicLibrary + true + v140 + Unicode + true + + + DynamicLibrary + false + v140 + true + Unicode + true + + + DynamicLibrary + true + v140 + Unicode + true + + + DynamicLibrary + false + v140 + true + Unicode + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;CPPINEROP_EXPORTS;%(PreprocessorDefinitions) + ./;$(SolutionDir)lib\local\LandmarkDetector\include;$(SolutionDir)lib\local\FaceAnalyser\include;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + + + Level3 + Disabled + _DEBUG;_WINDOWS;_USRDLL;CPPINEROP_EXPORTS;%(PreprocessorDefinitions) + ./;$(SolutionDir)lib\local\LandmarkDetector\include;$(SolutionDir)lib\local\FaceAnalyser\include;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;CPPINEROP_EXPORTS;%(PreprocessorDefinitions) + ./;$(SolutionDir)lib\local\LandmarkDetector\include;$(SolutionDir)lib\local\FaceAnalyser\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_WINDOWS;_USRDLL;CPPINEROP_EXPORTS;%(PreprocessorDefinitions) + ./;$(SolutionDir)lib\local\LandmarkDetector\include;$(SolutionDir)lib\local\FaceAnalyser\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + + + + + + + + + + + {b47a5f12-2567-44e9-ae49-35763ec82149} + + + {0cec6a75-17ba-4dc5-9405-c74154921e60} + + + {0e7fc556-0e80-45ea-a876-dde4c2fedcd7} + + + {bdc1d107-de17-4705-8e7b-cdde8bfb2bf8} + + + + + + ..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\WindowsBase.dll + + + + + + \ No newline at end of file diff --git a/lib/local/CppInerop/CppInerop.vcxproj.filters b/lib/local/CppInerop/CppInerop.vcxproj.filters new file mode 100644 index 00000000..6d1324c1 --- /dev/null +++ b/lib/local/CppInerop/CppInerop.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/lib/local/CppInerop/CppInterop.cpp b/lib/local/CppInerop/CppInterop.cpp new file mode 100644 index 00000000..9420aacf --- /dev/null +++ b/lib/local/CppInerop/CppInterop.cpp @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED AS IS FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called open source software licenses (Open Source +// Components), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensees request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltruaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltruaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +// This is the main DLL file. +#include "LandmarkDetectorInterop.h" +#include "FaceAnalyserInterop.h" +#include "OpenCVWrappers.h" +#include "CameraInterop.h" diff --git a/lib/local/CppInerop/FaceAnalyserInterop.h b/lib/local/CppInerop/FaceAnalyserInterop.h new file mode 100644 index 00000000..8aa4e382 --- /dev/null +++ b/lib/local/CppInerop/FaceAnalyserInterop.h @@ -0,0 +1,463 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED AS IS FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called open source software licenses (Open Source +// Components), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensees request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltruaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltruaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +// FaceAnalyser_Interop.h +#ifndef __FACE_ANALYSER_INTEROP_h_ +#define __FACE_ANALYSER_INTEROP_h_ + +#pragma once + +// Include all the unmanaged things we need. +#pragma managed + +#include +#include + +#pragma unmanaged + +#include +#include "opencv2/objdetect.hpp" +#include "opencv2/calib3d.hpp" +#include +#include +#include + +#include +#include +#include +#include +#include + +// Allows to overcome boost name clash stuff with C++ CLI +#ifdef __cplusplus_cli +#define generic __identifier(generic) +#endif + +// Boost stuff +#include +#include + +#ifdef __cplusplus_cli +#undef generic +#endif + +#pragma managed + +namespace FaceAnalyser_Interop { + +public ref class FaceAnalyserManaged +{ + +private: + + FaceAnalysis::FaceAnalyser* face_analyser; + + // The actual descriptors (for visualisation and output) + cv::Mat_* hog_features; + cv::Mat* aligned_face; + cv::Mat* visualisation; + cv::Mat* tracked_face; + + // Variables used for recording things + std::ofstream* hog_output_file; + std::string* align_output_dir; + int* num_rows; + int* num_cols; + bool* good_frame; + cv::VideoWriter* tracked_vid_writer; + + // Variable storing gaze for recording + + // Absolute gaze direction + cv::Point3f* gazeDirection0; + cv::Point3f* gazeDirection1; + + cv::Point3f* pupil_left; + cv::Point3f* pupil_right; + +public: + + FaceAnalyserManaged(System::String^ root, bool dynamic) + { + + vector orientation_bins; + orientation_bins.push_back(cv::Vec3d(0,0,0)); + double scale = 0.7; + int width = 112; + int height = 112; + + string root_std = msclr::interop::marshal_as(root); + + // TODO diff paths and locations for the demo mode + boost::filesystem::path tri_loc = boost::filesystem::path(root_std) / "model" / "tris_68_full.txt"; + boost::filesystem::path au_loc; + if(dynamic) + { + au_loc = boost::filesystem::path(root_std) / "AU_predictors" / "AU_all_best.txt"; + } + else + { + au_loc = boost::filesystem::path(root_std) / "AU_predictors" / "AU_all_static.txt"; + } + + face_analyser = new FaceAnalysis::FaceAnalyser(orientation_bins, scale, width, height, au_loc.string(), tri_loc.string()); + + hog_features = new cv::Mat_(); + + aligned_face = new cv::Mat(); + visualisation = new cv::Mat(); + tracked_face = new cv::Mat(); + + num_rows = new int; + num_cols = new int; + + good_frame = new bool; + + align_output_dir = new string(); + + hog_output_file = new std::ofstream(); + + gazeDirection0 = new cv::Point3f(); + gazeDirection1 = new cv::Point3f(); + + pupil_left = new cv::Point3f(); + pupil_right = new cv::Point3f(); + } + + void SetupAlignedImageRecording(System::String^ directory) + { + *align_output_dir = msclr::interop::marshal_as(directory); + } + + void SetupHOGRecording(System::String^ file) + { + // Create the file for recording + hog_output_file->open(msclr::interop::marshal_as(file), ios_base::out | ios_base::binary); + } + + void SetupTrackingRecording(System::String^ file, int width, int height, double fps) + { + tracked_vid_writer = new cv::VideoWriter(msclr::interop::marshal_as(file), CV_FOURCC('D', 'I', 'V', 'X'), fps, cv::Size(width, height)); + } + + void StopHOGRecording() + { + hog_output_file->close(); + } + + void StopTrackingRecording() + { + tracked_vid_writer->release(); + } + + void RecordAlignedFrame(int frame_num) + { + char name[100]; + + // output the frame number + sprintf(name, "frame_det_%06d.png", frame_num); + + string out_file = (boost::filesystem::path(*align_output_dir) / boost::filesystem::path(name)).string(); + imwrite(out_file, *aligned_face); + } + + void RecordHOGFrame() + { + // Using FHOGs, hence 31 channels + int num_channels = 31; + + hog_output_file->write((char*)(num_cols), 4); + hog_output_file->write((char*)(num_rows), 4); + hog_output_file->write((char*)(&num_channels), 4); + + // Not the best way to store a bool, but will be much easier to read it + float good_frame_float; + if(good_frame) + good_frame_float = 1; + else + good_frame_float = -1; + + hog_output_file->write((char*)(&good_frame_float), 4); + + cv::MatConstIterator_ descriptor_it = hog_features->begin(); + + for(int y = 0; y < *num_cols; ++y) + { + for(int x = 0; x < *num_rows; ++x) + { + for(unsigned int o = 0; o < 31; ++o) + { + + float hog_data = (float)(*descriptor_it++); + hog_output_file->write((char*)&hog_data, 4); + } + } + } + + } + + void RecordTrackedFace() + { + tracked_vid_writer->write(*tracked_face); + } + + void AddNextFrame(OpenCVWrappers::RawImage^ frame, CppInterop::LandmarkDetector::CLNF^ clnf, double fx, double fy, double cx, double cy, bool online, bool vis_hog, bool vis_tracked) { + + face_analyser->AddNextFrame(frame->Mat, *clnf->getCLM(), 0, online, vis_hog); + + face_analyser->GetLatestHOG(*hog_features, *num_rows, *num_cols); + + face_analyser->GetLatestAlignedFace(*aligned_face); + + *good_frame = clnf->clnf->detection_success; + + if(vis_hog) + { + *visualisation = face_analyser->GetLatestHOGDescriptorVisualisation(); + } + + if(vis_tracked) + { + if(frame->Mat.cols != tracked_face->cols && frame->Mat.rows != tracked_face->rows) + { + *tracked_face = frame->Mat.clone(); + } + else + { + frame->Mat.clone().copyTo(*tracked_face); + } + + if(clnf->clnf->detection_success) + { + ::LandmarkDetector::Draw(*tracked_face, *clnf->clnf); + } + tracked_face->deallocate(); + } + + // After the AUs have been detected do some gaze estimation as well + FaceAnalysis::EstimateGaze(*clnf->getCLM(), *gazeDirection0, fx, fy, cx, cy, true); + FaceAnalysis::EstimateGaze(*clnf->getCLM(), *gazeDirection1, fx, fy, cx, cy, false); + + // Grab pupil locations + int part_left = -1; + int part_right = -1; + for (size_t i = 0; i < clnf->getCLM()->hierarchical_models.size(); ++i) + { + if (clnf->getCLM()->hierarchical_model_names[i].compare("left_eye_28") == 0) + { + part_left = i; + } + if (clnf->getCLM()->hierarchical_model_names[i].compare("right_eye_28") == 0) + { + part_right = i; + } + } + + cv::Mat_ eyeLdmks3d_left = clnf->getCLM()->hierarchical_models[part_left].GetShape(fx, fy, cx, cy); + cv::Point3f pupil_left_h = FaceAnalysis::GetPupilPosition(eyeLdmks3d_left); + pupil_left->x = pupil_left_h.x; pupil_left->y = pupil_left_h.y; pupil_left->z = pupil_left_h.z; + + cv::Mat_ eyeLdmks3d_right = clnf->getCLM()->hierarchical_models[part_right].GetShape(fx, fy, cx, cy); + cv::Point3f pupil_right_h = FaceAnalysis::GetPupilPosition(eyeLdmks3d_right); + pupil_right->x = pupil_right_h.x; pupil_right->y = pupil_right_h.y; pupil_right->z = pupil_right_h.z; + } + + System::Tuple^, System::Tuple^>^ GetGazeCamera() + { + + auto gaze0 = gcnew System::Tuple(gazeDirection0->x, gazeDirection0->y, gazeDirection0->z); + auto gaze1 = gcnew System::Tuple(gazeDirection1->x, gazeDirection1->y, gazeDirection1->z); + + return gcnew System::Tuple^, System::Tuple^>(gaze0, gaze1); + + } + + System::Collections::Generic::List^>^ CalculateGazeLines(float fx, float fy, float cx, float cy) + { + + cv::Mat_ cameraMat = (cv::Mat_(3, 3) << fx, 0, cx, 0, fy, cy, 0, 0, 0); + + vector points_left; + points_left.push_back(cv::Point3d(*pupil_left)); + points_left.push_back(cv::Point3d(*pupil_left + *gazeDirection0*40.0)); + + vector points_right; + points_right.push_back(cv::Point3d(*pupil_right)); + points_right.push_back(cv::Point3d(*pupil_right + *gazeDirection1*40.0)); + + vector imagePoints_left; + projectPoints(points_left, cv::Mat::eye(3, 3, cv::DataType::type), cv::Mat::zeros(1, 3, cv::DataType::type), cameraMat, cv::Mat::zeros(4, 1, cv::DataType::type), imagePoints_left); + + vector imagePoints_right; + projectPoints(points_right, cv::Mat::eye(3, 3, cv::DataType::type), cv::Mat::zeros(1, 3, cv::DataType::type), cameraMat, cv::Mat::zeros(4, 1, cv::DataType::type), imagePoints_right); + + auto lines = gcnew System::Collections::Generic::List^>(); + lines->Add(gcnew System::Tuple(System::Windows::Point(imagePoints_left[0].x, imagePoints_left[0].y), System::Windows::Point(imagePoints_left[1].x, imagePoints_left[1].y))); + lines->Add(gcnew System::Tuple(System::Windows::Point(imagePoints_right[0].x, imagePoints_right[0].y), System::Windows::Point(imagePoints_right[1].x, imagePoints_right[1].y))); + + return lines; + } + + + System::Collections::Generic::List^ GetClassActionUnitsNames() + { + auto names = face_analyser->GetAUClassNames(); + + auto names_ret = gcnew System::Collections::Generic::List(); + + for(std::string name : names) + { + names_ret->Add(gcnew System::String(name.c_str())); + } + + return names_ret; + + } + + System::Collections::Generic::List^ GetRegActionUnitsNames() + { + auto names = face_analyser->GetAURegNames(); + + auto names_ret = gcnew System::Collections::Generic::List(); + + for(std::string name : names) + { + names_ret->Add(gcnew System::String(name.c_str())); + } + + return names_ret; + + } + + System::Collections::Generic::Dictionary^ GetCurrentAUsClass() + { + auto classes = face_analyser->GetCurrentAUsClass(); + auto au_classes = gcnew System::Collections::Generic::Dictionary(); + + for(auto p: classes) + { + au_classes->Add(gcnew System::String(p.first.c_str()), p.second); + } + return au_classes; + } + + System::Collections::Generic::Dictionary^ GetCurrentAUsReg() + { + auto preds = face_analyser->GetCurrentAUsReg(); + auto au_preds = gcnew System::Collections::Generic::Dictionary(); + + for(auto p: preds) + { + au_preds->Add(gcnew System::String(p.first.c_str()), p.second); + } + return au_preds; + } + + OpenCVWrappers::RawImage^ GetLatestAlignedFace() { + OpenCVWrappers::RawImage^ face_aligned_image = gcnew OpenCVWrappers::RawImage(*aligned_face); + return face_aligned_image; + } + + OpenCVWrappers::RawImage^ GetLatestHOGDescriptorVisualisation() { + OpenCVWrappers::RawImage^ HOG_vis_image = gcnew OpenCVWrappers::RawImage(*visualisation); + return HOG_vis_image; + } + + void Reset() + { + face_analyser->Reset(); + } + + // Finalizer. Definitely called before Garbage Collection, + // but not automatically called on explicit Dispose(). + // May be called multiple times. + !FaceAnalyserManaged() + { + delete hog_features; + delete aligned_face; + delete visualisation; + delete num_cols; + delete num_rows; + delete hog_output_file; + delete good_frame; + delete align_output_dir; + delete face_analyser; + delete tracked_face; + + delete gazeDirection0; + delete gazeDirection1; + + delete pupil_left; + delete pupil_right; + + if(tracked_vid_writer != 0) + { + delete tracked_vid_writer; + } + } + + // Destructor. Called on explicit Dispose() only. + ~FaceAnalyserManaged() + { + this->!FaceAnalyserManaged(); + } + +}; +} + +#endif \ No newline at end of file diff --git a/lib/local/CppInerop/LandmarkDetectorInterop.h b/lib/local/CppInerop/LandmarkDetectorInterop.h new file mode 100644 index 00000000..ea12dc1d --- /dev/null +++ b/lib/local/CppInerop/LandmarkDetectorInterop.h @@ -0,0 +1,395 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED AS IS FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called open source software licenses (Open Source +// Components), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensees request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltruaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltruaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef __LANDMARK_DETECTOR_INTEROP_h_ +#define __LANDMARK_DETECTOR_INTEROP_h_ + +#pragma once + +#pragma managed +#include +#include + +#pragma unmanaged + +// Include all the unmanaged things we need. + +#include +#include "opencv2/objdetect.hpp" +#include "opencv2/calib3d.hpp" +#include +#include +#include + +#include + +#include + +#include +#include + +#pragma managed + +namespace CppInterop { + + namespace LandmarkDetector { + + public ref class FaceModelParameters + { + public: + ::LandmarkDetector::FaceModelParameters* params; + + public: + + // Initialise the parameters + FaceModelParameters(System::String^ root) + { + std::string root_std = msclr::interop::marshal_as(root); + vector args; + args.push_back(root_std); + + params = new ::LandmarkDetector::FaceModelParameters(args); + + params->track_gaze = true; + } + + // TODO this could have optimize for demo mode (also could appropriately update sigma, reg_factor as well) + void optimiseForVideo() + { + params->window_sizes_small = vector(4); + params->window_sizes_init = vector(4); + + // For fast tracking + params->window_sizes_small[0] = 0; + params->window_sizes_small[1] = 9; + params->window_sizes_small[2] = 7; + params->window_sizes_small[3] = 5; + + // Just for initialisation + params->window_sizes_init.at(0) = 11; + params->window_sizes_init.at(1) = 9; + params->window_sizes_init.at(2) = 7; + params->window_sizes_init.at(3) = 5; + + // For first frame use the initialisation + params->window_sizes_current = params->window_sizes_init; + + params->sigma = 1.5; + params->reg_factor = 25; + params->weight_factor = 0; + } + + // TODO adapt here as well + void optimiseForImages() + { + params->window_sizes_init = vector(4); + params->window_sizes_init[0] = 15; + params->window_sizes_init[1] = 13; + params->window_sizes_init[2] = 11; + params->window_sizes_init[3] = 9; + + params->multi_view = true; + + params->sigma = 1.25; + params->reg_factor = 35; + params->weight_factor = 2.5; + params->num_optimisation_iteration = 10; + } + + ::LandmarkDetector::FaceModelParameters* getParams() { + return params; + } + + ~FaceModelParameters() + { + delete params; + } + + }; + + public ref class CLNF + { + public: + + // A pointer to the CLNF landmark detector + ::LandmarkDetector::CLNF* clnf; + + public: + + // Wrapper functions for the relevant CLNF functionality + CLNF() : clnf(new ::LandmarkDetector::CLNF()) { } + + CLNF(FaceModelParameters^ params) + { + clnf = new ::LandmarkDetector::CLNF(params->getParams()->model_location); + } + + ~CLNF() + { + delete clnf; + } + + ::LandmarkDetector::CLNF* getCLM() { + return clnf; + } + + void Reset() { + clnf->Reset(); + } + + void Reset(double x, double y) { + clnf->Reset(x, y); + } + + + double GetConfidence() + { + return clnf->detection_certainty; + } + + bool DetectLandmarksInVideo(OpenCVWrappers::RawImage^ image, FaceModelParameters^ clmParams) { + return ::LandmarkDetector::DetectLandmarksInVideo(image->Mat, *clnf, *clmParams->getParams()); + } + + bool DetectFaceLandmarksInImage(OpenCVWrappers::RawImage^ image, FaceModelParameters^ clmParams) { + return ::LandmarkDetector::DetectLandmarksInImage(image->Mat, *clnf, *clmParams->getParams()); + } + + System::Collections::Generic::List^>^>^ DetectMultiFaceLandmarksInImage(OpenCVWrappers::RawImage^ image, FaceModelParameters^ clmParams) { + + auto all_landmarks = gcnew System::Collections::Generic::List^>^>(); + + // Detect faces in an image + vector > face_detections; + + vector confidences; + + // TODO this should be pre-allocated as now it might be a bit too slow + dlib::frontal_face_detector face_detector_hog = dlib::get_frontal_face_detector(); + + ::LandmarkDetector::DetectFacesHOG(face_detections, image->Mat, face_detector_hog, confidences); + + // Detect landmarks around detected faces + int face_det = 0; + // perform landmark detection for every face detected + for(size_t face=0; face < face_detections.size(); ++face) + { + cv::Mat depth; + // if there are multiple detections go through them + bool success = ::LandmarkDetector::DetectLandmarksInImage(image->Mat, depth, face_detections[face], *clnf, *clmParams->getParams()); + + auto landmarks_curr = gcnew System::Collections::Generic::List^>(); + if(clnf->detected_landmarks.cols == 1) + { + int n = clnf->detected_landmarks.rows / 2; + for(int i = 0; i < n; ++i) + { + landmarks_curr->Add(gcnew System::Tuple(clnf->detected_landmarks.at(i,0), clnf->detected_landmarks.at(i+n,0))); + } + } + else + { + int n = clnf->detected_landmarks.cols / 2; + for(int i = 0; i < clnf->detected_landmarks.cols; ++i) + { + landmarks_curr->Add(gcnew System::Tuple(clnf->detected_landmarks.at(0,i), clnf->detected_landmarks.at(0,i+1))); + } + } + all_landmarks->Add(landmarks_curr); + + } + + return all_landmarks; + } + + void GetCorrectedPoseCamera(System::Collections::Generic::List^ pose, double fx, double fy, double cx, double cy) { + auto pose_vec = ::LandmarkDetector::GetCorrectedPoseCamera(*clnf, fx, fy, cx, cy); + pose->Clear(); + for(int i = 0; i < 6; ++i) + { + pose->Add(pose_vec[i]); + } + } + + void GetCorrectedPoseWorld(System::Collections::Generic::List^ pose, double fx, double fy, double cx, double cy) { + auto pose_vec = ::LandmarkDetector::GetCorrectedPoseWorld(*clnf, fx, fy, cx, cy); + pose->Clear(); + for(int i = 0; i < 6; ++i) + { + pose->Add(pose_vec[i]); + } + } + + System::Collections::Generic::List^>^ CalculateLandmarks() { + vector vecLandmarks = ::LandmarkDetector::CalculateLandmarks(*clnf); + + auto landmarks = gcnew System::Collections::Generic::List^>(); + for(cv::Point2d p : vecLandmarks) { + landmarks->Add(gcnew System::Tuple(p.x, p.y)); + } + + return landmarks; + } + + System::Collections::Generic::List^ Calculate3DLandmarks(double fx, double fy, double cx, double cy) { + + cv::Mat_ shape3D = clnf->GetShape(fx, fy, cx, cy); + + auto landmarks_3D = gcnew System::Collections::Generic::List(); + + for(int i = 0; i < shape3D.cols; ++i) + { + landmarks_3D->Add(System::Windows::Media::Media3D::Point3D(shape3D.at(0, i), shape3D.at(1, i), shape3D.at(2, i))); + } + + return landmarks_3D; + } + + + // Static functions from the CLMTracker namespace. + void DrawLandmarks(OpenCVWrappers::RawImage^ img, System::Collections::Generic::List^ landmarks) { + + vector vecLandmarks; + + for(int i = 0; i < landmarks->Count; i++) { + System::Windows::Point p = landmarks[i]; + vecLandmarks.push_back(cv::Point(p.X, p.Y)); + } + + ::LandmarkDetector::DrawLandmarks(img->Mat, vecLandmarks); + } + + + System::Collections::Generic::List^>^ CalculateBox(float fx, float fy, float cx, float cy) { + + cv::Vec6d pose = ::LandmarkDetector::GetCorrectedPoseWorld(*clnf, fx,fy, cx, cy); + + vector> vecLines = ::LandmarkDetector::CalculateBox(pose, fx, fy, cx, cy); + + auto lines = gcnew System::Collections::Generic::List^>(); + + for(pair line : vecLines) { + lines->Add(gcnew System::Tuple(System::Windows::Point(line.first.x, line.first.y), System::Windows::Point(line.second.x, line.second.y))); + } + + return lines; + } + + void DrawBox(System::Collections::Generic::List^>^ lines, OpenCVWrappers::RawImage^ image, double r, double g, double b, int thickness) { + cv::Scalar color = cv::Scalar(r,g,b,1); + + vector> vecLines; + + for(int i = 0; i < lines->Count; i++) { + System::Tuple^ points = lines[i]; + vecLines.push_back(pair(cv::Point(points->Item1.X, points->Item1.Y), cv::Point(points->Item2.X, points->Item2.Y))); + } + + ::LandmarkDetector::DrawBox(vecLines, image->Mat, color, thickness); + } + + int GetNumPoints() + { + return clnf->pdm.NumberOfPoints(); + } + + int GetNumModes() + { + return clnf->pdm.NumberOfModes(); + } + + // Getting the non-rigid shape parameters describing the facial expression + System::Collections::Generic::List^ GetNonRigidParams() + { + auto non_rigid_params = gcnew System::Collections::Generic::List(); + + for (int i = 0; i < clnf->params_local.rows; ++i) + { + non_rigid_params->Add(clnf->params_local.at(i)); + } + + return non_rigid_params; + } + + // Getting the rigid shape parameters describing face scale rotation and translation (scale,rotx,roty,rotz,tx,ty) + System::Collections::Generic::List^ GetRigidParams() + { + auto rigid_params = gcnew System::Collections::Generic::List(); + + for (size_t i = 0; i < 6; ++i) + { + rigid_params->Add(clnf->params_global[i]); + } + return rigid_params; + } + + // Rigid params followed by non-rigid ones + System::Collections::Generic::List^ GetParams() + { + auto all_params = GetRigidParams(); + all_params->AddRange(GetNonRigidParams()); + return all_params; + } + + }; + + } + +} + +#endif \ No newline at end of file diff --git a/lib/local/CppInerop/OpenCVWrappers.h b/lib/local/CppInerop/OpenCVWrappers.h new file mode 100644 index 00000000..de2907d6 --- /dev/null +++ b/lib/local/CppInerop/OpenCVWrappers.h @@ -0,0 +1,305 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, +// all rights reserved. +// +// THIS SOFTWARE IS PROVIDED AS IS FOR ACADEMIC USE ONLY AND ANY EXPRESS +// OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY. +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Notwithstanding the license granted herein, Licensee acknowledges that certain components +// of the Software may be covered by so-called open source software licenses (Open Source +// Components), which means any software licenses approved as open source licenses by the +// Open Source Initiative or any substantially similar licenses, including without limitation any +// license that, as a condition of distribution of the software licensed under such license, +// requires that the distributor make the software available in source code format. Licensor shall +// provide a list of Open Source Components for a particular version of the Software upon +// Licensees request. Licensee will comply with the applicable terms of such licenses and to +// the extent required by the licenses covering Open Source Components, the terms of such +// licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the +// licenses applicable to Open Source Components prohibit any of the restrictions in this +// License Agreement with respect to such Open Source Component, such restrictions will not +// apply to such Open Source Component. To the extent the terms of the licenses applicable to +// Open Source Components require Licensor to make an offer to provide source code or +// related information in connection with the Software, such offer is hereby made. Any request +// for source code or related information should be directed to cl-face-tracker-distribution@lists.cam.ac.uk +// Licensee acknowledges receipt of notices for the Open Source Components for the initial +// delivery of the Software. + +// * Any publications arising from the use of this software, including but +// not limited to academic journal and conference publications, technical +// reports and manuals, must cite at least one of the following works: +// +// OpenFace: an open source facial behavior analysis toolkit +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency +// in IEEE Winter Conference on Applications of Computer Vision, 2016 +// +// Rendering of Eyes for Eye-Shape Registration and Gaze Estimation +// Erroll Wood, Tadas Baltruaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// in IEEE International. Conference on Computer Vision (ICCV), 2015 +// +// Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection +// Tadas Baltruaitis, Marwa Mahmoud, and Peter Robinson +// in Facial Expression Recognition and Analysis Challenge, +// IEEE International Conference on Automatic Face and Gesture Recognition, 2015 +// +// Constrained Local Neural Fields for robust facial landmark detection in the wild. +// Tadas Baltruaitis, Peter Robinson, and Louis-Philippe Morency. +// in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. +// +/////////////////////////////////////////////////////////////////////////////// + +// OpenCVWrappers.h + +#pragma once + +#pragma managed + +#include + +#pragma unmanaged + +#include "cv.h" +#include "highgui.h" + +#include // Video write +#include // Video write + +#pragma managed + +#pragma make_public(cv::Mat) + +using namespace System::Windows; +using namespace System::Windows::Threading; +using namespace System::Windows::Media; +using namespace System::Windows::Media::Imaging; + +namespace OpenCVWrappers { + + public ref class RawImage : System::IDisposable + { + + private: + + cv::Mat* mat; + + static int refCount; + + + public: + + static int PixelFormatToType(PixelFormat fmt) + { + if (fmt == PixelFormats::Gray8) + return CV_8UC1; + else if(fmt == PixelFormats::Bgr24) + return CV_8UC3; + else if(fmt == PixelFormats::Bgra32) + return CV_8UC4; + else if(fmt == PixelFormats::Gray32Float) + return CV_32FC1; + else + throw gcnew System::Exception("Unsupported pixel format"); + } + + static PixelFormat TypeToPixelFormat(int type) + { + switch (type) { + case CV_8UC1: + return PixelFormats::Gray8; + case CV_8UC3: + return PixelFormats::Bgr24; + case CV_8UC4: + return PixelFormats::Bgra32; + case CV_32FC1: + return PixelFormats::Gray32Float; + default: + throw gcnew System::Exception("Unsupported image type"); + } + } + + static property int RefCount { + int get() { return refCount; } + } + + RawImage() + { + mat = new cv::Mat(); + refCount++; + } + + RawImage(const cv::Mat& m) + { + mat = new cv::Mat(m.clone()); + refCount++; + } + + RawImage(RawImage^ img) + { + mat = new cv::Mat(img->Mat.clone()); + refCount++; + } + + RawImage(int width, int height, int type) + { + mat = new cv::Mat(height, width, type); + refCount++; + } + + RawImage(int width, int height, PixelFormat format) + { + int type = RawImage::PixelFormatToType(format); + mat = new cv::Mat(height, width, type); + refCount++; + } + + // Finalizer. Definitely called before Garbage Collection, + // but not automatically called on explicit Dispose(). + // May be called multiple times. + !RawImage() + { + if (mat) + { + delete mat; + mat = NULL; + refCount--; + } + } + + // Destructor. Called on explicit Dispose() only. + ~RawImage() + { + this->!RawImage(); + } + + property int Width + { + int get() + { + return mat->cols; + } + } + + property int Height + { + int get() + { + return mat->rows; + } + } + + property int Stride + { + + int get() + { + return mat->step; + } + } + + property PixelFormat Format + { + PixelFormat get() + { + return TypeToPixelFormat(mat->type()); + } + } + + property cv::Mat& Mat + { + cv::Mat& get() + { + return *mat; + } + } + + property bool IsEmpty + { + bool get() + { + return !mat || mat->empty(); + } + } + + bool UpdateWriteableBitmap(WriteableBitmap^ bitmap) + { + if (bitmap == nullptr || bitmap->PixelWidth != Width || bitmap->PixelHeight != Height || bitmap->Format != Format) + return false; + else { + if (mat->data == NULL) { + cv::Mat zeros(bitmap->PixelHeight, bitmap->PixelWidth, PixelFormatToType(bitmap->Format), 0); + bitmap->WritePixels(Int32Rect(0, 0, Width, Height), System::IntPtr(zeros.data), Stride * Height * (Format.BitsPerPixel / 8), Stride, 0, 0); + } + else { + bitmap->WritePixels(Int32Rect(0, 0, Width, Height), System::IntPtr(mat->data), Stride * Height * (Format.BitsPerPixel / 8), Stride, 0, 0); + } + return true; + } + } + + WriteableBitmap^ CreateWriteableBitmap() + { + return gcnew WriteableBitmap(Width, Height, 72, 72, Format, nullptr); + } + + }; + + public ref class VideoWriter + { + private: + // OpenCV based video capture for reading from files + cv::VideoWriter* vc; + + public: + + VideoWriter(System::String^ location, int width, int height, double fps, bool colour) + { + + msclr::interop::marshal_context context; + std::string location_std_string = context.marshal_as(location); + + vc = new cv::VideoWriter(location_std_string, CV_FOURCC('D','I','V','X'), fps, cv::Size(width, height), colour); + + } + + // Return success + bool Write(RawImage^ img) + { + if(vc != nullptr && vc->isOpened()) + { + vc->write(img->Mat); + return true; + } + else + { + return false; + } + } + + // Finalizer. Definitely called before Garbage Collection, + // but not automatically called on explicit Dispose(). + // May be called multiple times. + !VideoWriter() + { + if(vc != nullptr) + { + vc->~VideoWriter(); + } + } + + // Destructor. Called on explicit Dispose() only. + ~VideoWriter() + { + this->!VideoWriter(); + } + + }; + +} diff --git a/lib/local/FaceAnalyser/include/GazeEstimation.h b/lib/local/FaceAnalyser/include/GazeEstimation.h index ac92be75..e7124f40 100644 --- a/lib/local/FaceAnalyser/include/GazeEstimation.h +++ b/lib/local/FaceAnalyser/include/GazeEstimation.h @@ -68,5 +68,8 @@ namespace FaceAnalysis void EstimateGaze(const LandmarkDetector::CLNF& clnf_model, cv::Point3f& gaze_absolute, float fx, float fy, float cx, float cy, bool left_eye); void DrawGaze(cv::Mat img, const LandmarkDetector::CLNF& clnf_model, cv::Point3f gazeVecAxisLeft, cv::Point3f gazeVecAxisRight, float fx, float fy, float cx, float cy); + // Some utilities + cv::Point3f GetPupilPosition(cv::Mat_ eyeLdmks3d); + } #endif \ No newline at end of file diff --git a/lib/local/FaceAnalyser/src/GazeEstimation.cpp b/lib/local/FaceAnalyser/src/GazeEstimation.cpp index 9ac0896f..4e31d965 100644 --- a/lib/local/FaceAnalyser/src/GazeEstimation.cpp +++ b/lib/local/FaceAnalyser/src/GazeEstimation.cpp @@ -96,7 +96,7 @@ cv::Point3f RaySphereIntersect(cv::Point3f rayOrigin, cv::Point3f rayDir, cv::Po return rayOrigin + rayDir * t; } -cv::Point3f GetPupilPosition(cv::Mat_ eyeLdmks3d){ +cv::Point3f FaceAnalysis::GetPupilPosition(cv::Mat_ eyeLdmks3d){ eyeLdmks3d = eyeLdmks3d.t(); diff --git a/matlab_runners/Action Unit Experiments/DISFA_valid_res.txt b/matlab_runners/Action Unit Experiments/DISFA_valid_res.txt index edbb1ee0..170f9fd5 100644 --- a/matlab_runners/Action Unit Experiments/DISFA_valid_res.txt +++ b/matlab_runners/Action Unit Experiments/DISFA_valid_res.txt @@ -1,12 +1,12 @@ -AU1 results - corr 0.740, ccc - 0.729 -AU2 results - corr 0.702, ccc - 0.623 -AU4 results - corr 0.847, ccc - 0.823 -AU5 results - corr 0.669, ccc - 0.662 -AU6 results - corr 0.641, ccc - 0.627 -AU9 results - corr 0.722, ccc - 0.703 -AU12 results - corr 0.857, ccc - 0.847 -AU15 results - corr 0.652, ccc - 0.639 +AU1 results - corr 0.740, ccc - 0.728 +AU2 results - corr 0.703, ccc - 0.624 +AU4 results - corr 0.845, ccc - 0.821 +AU5 results - corr 0.668, ccc - 0.661 +AU6 results - corr 0.638, ccc - 0.624 +AU9 results - corr 0.721, ccc - 0.702 +AU12 results - corr 0.863, ccc - 0.852 +AU15 results - corr 0.651, ccc - 0.637 AU17 results - corr 0.572, ccc - 0.506 -AU20 results - corr 0.507, ccc - 0.489 -AU25 results - corr 0.918, ccc - 0.916 -AU26 results - corr 0.552, ccc - 0.185 +AU20 results - corr 0.505, ccc - 0.487 +AU25 results - corr 0.921, ccc - 0.919 +AU26 results - corr 0.551, ccc - 0.184 diff --git a/matlab_runners/Feature Point Experiments/results/fps_yt.mat b/matlab_runners/Feature Point Experiments/results/fps_yt.mat index dfe869e4..399ddeab 100644 Binary files a/matlab_runners/Feature Point Experiments/results/fps_yt.mat and b/matlab_runners/Feature Point Experiments/results/fps_yt.mat differ diff --git a/matlab_runners/Feature Point Experiments/results/in-the-wild-res-no-outline.pdf b/matlab_runners/Feature Point Experiments/results/in-the-wild-res-no-outline.pdf index 59e0793c..cfddb547 100644 Binary files a/matlab_runners/Feature Point Experiments/results/in-the-wild-res-no-outline.pdf and b/matlab_runners/Feature Point Experiments/results/in-the-wild-res-no-outline.pdf differ diff --git a/matlab_runners/Feature Point Experiments/results/landmark_detections.mat b/matlab_runners/Feature Point Experiments/results/landmark_detections.mat index 50749372..bf9d68de 100644 Binary files a/matlab_runners/Feature Point Experiments/results/landmark_detections.mat and b/matlab_runners/Feature Point Experiments/results/landmark_detections.mat differ diff --git a/matlab_runners/Gaze Experiments/mpii_1500_errs.mat b/matlab_runners/Gaze Experiments/mpii_1500_errs.mat index 1d2dcd6b..098ca619 100644 Binary files a/matlab_runners/Gaze Experiments/mpii_1500_errs.mat and b/matlab_runners/Gaze Experiments/mpii_1500_errs.mat differ diff --git a/matlab_runners/Gaze Experiments/mpii_1500_errs.txt b/matlab_runners/Gaze Experiments/mpii_1500_errs.txt index 9618f9e4..6e6f0f99 100644 --- a/matlab_runners/Gaze Experiments/mpii_1500_errs.txt +++ b/matlab_runners/Gaze Experiments/mpii_1500_errs.txt @@ -1,2 +1,2 @@ Mean error, median error -9.944, 9.314 +9.977, 9.329 diff --git a/matlab_runners/Head Pose Experiments/results/Pose_OF.mat b/matlab_runners/Head Pose Experiments/results/Pose_OF.mat index 8a4272bd..08c9e0ed 100644 Binary files a/matlab_runners/Head Pose Experiments/results/Pose_OF.mat and b/matlab_runners/Head Pose Experiments/results/Pose_OF.mat differ